souffle  2.0.2-371-g6315b36
Public Member Functions | Private Member Functions | Static Private Member Functions
souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer Class Reference

Database normaliser for MST. More...

#include <MagicSet.h>

Inheritance diagram for souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer:
Inheritance graph
Collaboration diagram for souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer:
Collaboration graph

Public Member Functions

NormaliseDatabaseTransformerclone () const override
 
std::string getName () const override
 
- Public Member Functions inherited from souffle::ast::transform::Transformer
bool apply (TranslationUnit &translationUnit)
 
virtual ~Transformer ()=default
 

Private Member Functions

bool transform (TranslationUnit &translationUnit) override
 

Static Private Member Functions

static bool extractIDB (TranslationUnit &translationUnit)
 Separates the IDB from the EDB, so that they are disjoint. More...
 
static bool normaliseArguments (TranslationUnit &translationUnit)
 Normalise all arguments within each clause. More...
 
static bool partitionIO (TranslationUnit &translationUnit)
 Partitions the input and output relations. More...
 
static bool querifyOutputRelations (TranslationUnit &translationUnit)
 Extracts output relations into separate simple query relations, so that they are unused in any other rules. More...
 

Detailed Description

Database normaliser for MST.

Effects:

Definition at line 112 of file MagicSet.h.

Member Function Documentation

◆ clone()

NormaliseDatabaseTransformer* souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer::clone ( ) const
inlineoverridevirtual

Implements souffle::ast::transform::Transformer.

Definition at line 118 of file MagicSet.h.

118  {
119  return new NormaliseDatabaseTransformer();
120  }

◆ extractIDB()

bool souffle::ast::transform::NormaliseDatabaseTransformer::extractIDB ( TranslationUnit translationUnit)
staticprivate

Separates the IDB from the EDB, so that they are disjoint.

Program will no longer have input relations that appear as the head of clauses.

Definition at line 380 of file MagicSet.cpp.

381  : getClauses(program, rel->getQualifiedName())) {
382  visitDepthFirst(clause->getBodyLiterals(), [&](const Atom& /* atom */) { hasRules = true; });
383  }
384  return !hasRules;
385  };
386 
387  // Get all input relations that also have IDB rules attached
388  std::set<QualifiedName> inputRelationNames;
389  for (auto* rel : program.getRelations()) {
390  if (ioTypes.isInput(rel) && !isStrictlyEDB(rel)) {
391  assert(!ioTypes.isOutput(rel) && !ioTypes.isPrintSize(rel) &&
392  "input relations should not be output at this stage");
393  inputRelationNames.insert(rel->getQualifiedName());
394  }
395  }
396 
397  // Add a new intermediate non-input relation for each
398  // These will cover relation appearances in IDB rules
399  std::map<QualifiedName, QualifiedName> inputToIntermediate;
400  for (const auto& inputRelationName : inputRelationNames) {
401  // Give it a unique name
402  QualifiedName intermediateName(inputRelationName);
403  intermediateName.prepend("@interm_in");
404  inputToIntermediate[inputRelationName] = intermediateName;
405 
406  // Add the relation
407  auto intermediateRelation = souffle::clone(getRelation(program, inputRelationName));
408  intermediateRelation->setQualifiedName(intermediateName);
409  program.addRelation(std::move(intermediateRelation));
410  }
411 
412  // Rename them systematically
413  renameAtoms(program, inputToIntermediate);
414 
415  // Add the rule I' <- I
416  for (const auto& inputRelationName : inputRelationNames) {
417  auto queryHead = mk<Atom>(inputToIntermediate.at(inputRelationName));
418  auto queryLiteral = mk<Atom>(inputRelationName);
419 
420  // Give them identical arguments
421  const auto* inputRelation = getRelation(program, inputRelationName);
422  for (size_t i = 0; i < inputRelation->getArity(); i++) {
423  std::stringstream var;
424  var << "@query_x" << i;
425  queryHead->addArgument(mk<ast::Variable>(var.str()));
426  queryLiteral->addArgument(mk<ast::Variable>(var.str()));
427  }
428 
429  auto query = mk<Clause>(std::move(queryHead));
430  query->addToBody(std::move(queryLiteral));
431  program.addClause(std::move(query));
432  }
433 
434  return !inputRelationNames.empty();
435 }
436 
437 bool NormaliseDatabaseTransformer::querifyOutputRelations(TranslationUnit& translationUnit) {
438  Program& program = translationUnit.getProgram();
439 
440  // Helper method to check if a relation is a single-rule output query
441  auto isStrictlyOutput = [&](const Relation* rel) {

References souffle::ast::visitDepthFirst().

Here is the call graph for this function:

◆ getName()

std::string souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer::getName ( ) const
inlineoverridevirtual

Implements souffle::ast::transform::Transformer.

Definition at line 114 of file MagicSet.h.

114  {
115  return "NormaliseDatabaseTransformer";
116  }

◆ normaliseArguments()

bool souffle::ast::transform::NormaliseDatabaseTransformer::normaliseArguments ( TranslationUnit translationUnit)
staticprivate

Normalise all arguments within each clause.

All arguments in all clauses will now be either: (1) a variable, or (2) the RHS of a <var> = <arg> constraint

Definition at line 517 of file MagicSet.cpp.

521  : constraints(constraints), changeCount(changeCount) {}
522 
523  Own<Node> operator()(Own<Node> node) const override {
524  if (auto* aggr = dynamic_cast<Aggregator*>(node.get())) {
525  // Aggregator variable scopes should be maintained, so changes shouldn't propagate
526  // above this level.
527  std::set<Own<BinaryConstraint>> subConstraints;
528  argument_normaliser aggrUpdate(subConstraints, changeCount);
529  aggr->apply(aggrUpdate);
530 
531  // Add the constraints to this level
532  std::vector<Own<Literal>> newBodyLiterals;
533  for (const auto* lit : aggr->getBodyLiterals()) {
534  newBodyLiterals.push_back(souffle::clone(lit));
535  }
536  for (auto& constr : subConstraints) {
537  newBodyLiterals.push_back(souffle::clone(constr));
538  }
539 
540  // Update the node to reflect normalised aggregator
541  node = aggr->getTargetExpression() != nullptr
542  ? mk<Aggregator>(aggr->getBaseOperator(),
543  souffle::clone(aggr->getTargetExpression()),
544  std::move(newBodyLiterals))
545  : mk<Aggregator>(aggr->getBaseOperator(), nullptr, std::move(newBodyLiterals));
546  } else {
547  // Otherwise, just normalise children as usual.
548  node->apply(*this);
549  }
550 
551  // All non-variables should be normalised
552  if (auto* arg = dynamic_cast<Argument*>(node.get())) {
553  if (!isA<ast::Variable>(arg)) {
554  std::stringstream name;
555  name << "@abdul" << changeCount++;
556 
557  // Unnamed variables don't need a new constraint, just give them a name
558  if (isA<UnnamedVariable>(arg)) {
559  return mk<ast::Variable>(name.str());
560  }
561 
562  // Link other variables back to their original value with a `<var> = <arg>` constraint
563  constraints.insert(mk<BinaryConstraint>(
564  BinaryConstraintOp::EQ, mk<ast::Variable>(name.str()), souffle::clone(arg)));
565  return mk<ast::Variable>(name.str());
566  }
567  }
568  return node;
569  }
570  };
571 
572  // Transform each clause so that all arguments are:
573  // 1) a variable, or
574  // 2) the RHS of a `<var> = <arg>` constraint
575  bool changed = false;
576  for (auto* clause : program.getClauses()) {
577  int changeCount = 0;
578  std::set<Own<BinaryConstraint>> constraintsToAdd;
579  argument_normaliser update(constraintsToAdd, changeCount);
580 
581  // Apply to each clause head
582  clause->getHead()->apply(update);
583 
584  // Apply to each body literal that isn't already a `<var> = <arg>` constraint
585  for (Literal* lit : clause->getBodyLiterals()) {
586  if (auto* bc = dynamic_cast<BinaryConstraint*>(lit)) {
587  if (isEqConstraint(bc->getBaseOperator()) && isA<ast::Variable>(bc->getLHS())) {
588  continue;
589  }
590  }
591  lit->apply(update);
592  }
593 
594  // Also apply to each record
595  visitDepthFirst(*clause, [&](const RecordInit& rec) {
596  for (Argument* arg : rec.getArguments()) {
597  arg->apply(update);
598  }
599  });
600 
601  // Add each necessary new constraint to the clause
602  for (auto& constraint : constraintsToAdd) {
603  clause->addToBody(souffle::clone(constraint));
604  }
605 
606  changed |= changeCount != 0;
607  }
608 
609  return changed;
610 }
611 
613  const QualifiedName& relName, const std::string& adornmentMarker) {
614  if (adornmentMarker == "") return relName;
615  QualifiedName adornmentID(relName);
616  std::stringstream adornmentMarkerRepr;

◆ partitionIO()

bool souffle::ast::transform::NormaliseDatabaseTransformer::partitionIO ( TranslationUnit translationUnit)
staticprivate

Partitions the input and output relations.

Program will no longer have relations that are both input and output.

Definition at line 312 of file MagicSet.cpp.

312  : program.getRelations()) {
313  if (ioTypes.isInput(rel) && (ioTypes.isOutput(rel) || ioTypes.isPrintSize(rel))) {
314  relationsToSplit.insert(rel->getQualifiedName());
315  }
316  }
317 
318  // For each of these relations I, add a new relation I' that's input instead.
319  // The old relation I is no longer input, but copies over the data from I'.
320  for (auto relName : relationsToSplit) {
321  const auto* rel = getRelation(program, relName);
322  assert(rel != nullptr && "relation does not exist");
323  auto newRelName = QualifiedName(relName);
324  newRelName.prepend("@split_in");
325 
326  // Create a new intermediate input relation, I'
327  auto newRelation = mk<Relation>(newRelName);
328  for (const auto* attr : rel->getAttributes()) {
329  newRelation->addAttribute(souffle::clone(attr));
330  }
331 
332  // Add the rule I <- I'
333  auto newClause = mk<Clause>();
334  auto newHeadAtom = mk<Atom>(relName);
335  auto newBodyAtom = mk<Atom>(newRelName);
336  for (size_t i = 0; i < rel->getArity(); i++) {
337  std::stringstream varName;
338  varName << "@var" << i;
339  newHeadAtom->addArgument(mk<ast::Variable>(varName.str()));
340  newBodyAtom->addArgument(mk<ast::Variable>(varName.str()));
341  }
342  newClause->setHead(std::move(newHeadAtom));
343  newClause->addToBody(std::move(newBodyAtom));
344 
345  // New relation I' should be input, original should not
346  std::set<const Directive*> iosToDelete;
347  std::set<Own<Directive>> iosToAdd;
348  for (const auto* io : program.getDirectives()) {
349  if (io->getQualifiedName() == relName && io->getType() == ast::DirectiveType::input) {
350  // New relation inherits the old input rules
351  auto newIO = souffle::clone(io);
352  newIO->setQualifiedName(newRelName);
353  iosToAdd.insert(std::move(newIO));
354 
355  // Original no longer has them
356  iosToDelete.insert(io);
357  }
358  }
359  for (const auto* io : iosToDelete) {
360  program.removeDirective(io);
361  }
362  for (auto& io : iosToAdd) {
363  program.addDirective(souffle::clone(io));
364  }
365 
366  // Add in the new relation and the copy clause
367  program.addRelation(std::move(newRelation));
368  program.addClause(std::move(newClause));
369  }
370 
371  return !relationsToSplit.empty();
372 }
373 
374 bool NormaliseDatabaseTransformer::extractIDB(TranslationUnit& translationUnit) {
375  Program& program = translationUnit.getProgram();
376  const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
377 
378  // Helper method to check if an input relation has no associated rules

References rel().

Here is the call graph for this function:

◆ querifyOutputRelations()

bool souffle::ast::transform::NormaliseDatabaseTransformer::querifyOutputRelations ( TranslationUnit translationUnit)
staticprivate

Extracts output relations into separate simple query relations, so that they are unused in any other rules.

Programs will only contain output relations which: (1) have exactly one rule defining them (2) do not appear in other rules

Definition at line 443 of file MagicSet.cpp.

445  : program.getClauses()) {
446  // Check if the relation is used in the body of any rules
447  visitDepthFirst(clause->getBodyLiterals(), [&](const Atom& atom) {
448  if (atom.getQualifiedName() == rel->getQualifiedName()) {
449  strictlyOutput = false;
450  }
451  });
452 
453  // Keep track of number of rules defining the relation
454  if (clause->getHead()->getQualifiedName() == rel->getQualifiedName()) {
455  ruleCount++;
456  }
457  }
458 
459  return strictlyOutput && ruleCount <= 1;
460  };
461 
462  // Get all output relations that need to be normalised
463  const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
464  std::set<QualifiedName> outputRelationNames;
465  for (auto* rel : program.getRelations()) {
466  if ((ioTypes.isOutput(rel) || ioTypes.isPrintSize(rel)) && !isStrictlyOutput(rel)) {
467  assert(!ioTypes.isInput(rel) && "output relations should not be input at this stage");
468  outputRelationNames.insert(rel->getQualifiedName());
469  }
470  }
471 
472  // Add a new intermediate non-output relation for each
473  // These will cover relation appearances in intermediate rules
474  std::map<QualifiedName, QualifiedName> outputToIntermediate;
475  for (const auto& outputRelationName : outputRelationNames) {
476  // Give it a unique name
477  QualifiedName intermediateName(outputRelationName);
478  intermediateName.prepend("@interm_out");
479  outputToIntermediate[outputRelationName] = intermediateName;
480 
481  // Add the relation
482  auto intermediateRelation = souffle::clone(getRelation(program, outputRelationName));
483  intermediateRelation->setQualifiedName(intermediateName);
484  program.addRelation(std::move(intermediateRelation));
485  }
486 
487  // Rename them systematically
488  renameAtoms(program, outputToIntermediate);
489 
490  // Add the rule I <- I'
491  for (const auto& outputRelationName : outputRelationNames) {
492  auto queryHead = mk<Atom>(outputRelationName);
493  auto queryLiteral = mk<Atom>(outputToIntermediate.at(outputRelationName));
494 
495  // Give them identical arguments
496  const auto* outputRelation = getRelation(program, outputRelationName);
497  for (size_t i = 0; i < outputRelation->getArity(); i++) {
498  std::stringstream var;
499  var << "@query_x" << i;
500  queryHead->addArgument(mk<ast::Variable>(var.str()));
501  queryLiteral->addArgument(mk<ast::Variable>(var.str()));
502  }
503  auto query = mk<Clause>(std::move(queryHead));
504  query->addToBody(std::move(queryLiteral));
505  program.addClause(std::move(query));
506  }
507 
508  return !outputRelationNames.empty();
509 }
510 
511 bool NormaliseDatabaseTransformer::normaliseArguments(TranslationUnit& translationUnit) {
512  Program& program = translationUnit.getProgram();
513 
514  // Replace all non-variable-arguments nested inside the node with named variables
515  // Also, keeps track of constraints to add to keep the clause semantically equivalent

References souffle::ast::visitDepthFirst().

Here is the call graph for this function:

◆ transform()

bool souffle::ast::transform::NormaliseDatabaseTransformer::transform ( TranslationUnit translationUnit)
overrideprivatevirtual

(1) Partition input and output relations

(2) Separate the IDB from the EDB

(3) Normalise arguments within each clause

(4) Querify output relations

Implements souffle::ast::transform::Transformer.

Definition at line 290 of file MagicSet.cpp.

306  {
307  Program& program = translationUnit.getProgram();
308  const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
309 
310  // Get all relations that are both input and output

The documentation for this class was generated from the following files:
souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer::querifyOutputRelations
static bool querifyOutputRelations(TranslationUnit &translationUnit)
Extracts output relations into separate simple query relations, so that they are unused in any other ...
Definition: MagicSet.cpp:443
souffle::ast::renameAtoms
bool renameAtoms(Node &node, const std::map< QualifiedName, QualifiedName > &oldToNew)
Rename all atoms hat appear in a node to a given name.
Definition: Utils.cpp:307
souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer::normaliseArguments
static bool normaliseArguments(TranslationUnit &translationUnit)
Normalise all arguments within each clause.
Definition: MagicSet.cpp:517
souffle::BinaryConstraintOp::EQ
@ EQ
souffle::ast::transform::NormaliseDatabaseTransformer
MagicSetTransformer::NormaliseDatabaseTransformer NormaliseDatabaseTransformer
Definition: MagicSet.cpp:60
souffle::ast::transform::MagicSetTransformer::AdornDatabaseTransformer::getAdornmentID
static QualifiedName getAdornmentID(const QualifiedName &relName, const std::string &adornmentMarker)
Get the unique identifier corresponding to an adorned predicate.
Definition: MagicSet.cpp:618
souffle::ast::transform::MagicSetTransformer::NormaliseDatabaseTransformer::extractIDB
static bool extractIDB(TranslationUnit &translationUnit)
Separates the IDB from the EDB, so that they are disjoint.
Definition: MagicSet.cpp:380
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::mk
Own< A > mk(Args &&... xs)
Definition: ContainerUtil.h:48
i
size_t i
Definition: json11.h:663
souffle::ast::getRelation
Relation * getRelation(const Program &program, const QualifiedName &name)
Returns the relation with the given name in the program.
Definition: Utils.cpp:101
souffle::ast::DirectiveType::input
@ input
souffle::ast::getClauses
std::vector< Clause * > getClauses(const Program &program, const QualifiedName &relationName)
Returns a vector of clauses in the program describing the relation with the given name.
Definition: Utils.cpp:77
souffle::isEqConstraint
bool isEqConstraint(const BinaryConstraintOp constraintOp)
Definition: BinaryConstraintOps.h:124
std
Definition: Brie.h:3053
souffle::ast::visitDepthFirst
void visitDepthFirst(const Node &root, Visitor< R, Ps... > &visitor, Args &... args)
A utility function visiting all nodes within the ast rooted by the given node recursively in a depth-...
Definition: Visitor.h:273
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086