souffle  2.0.2-371-g6315b36
MagicSet.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2017, The Souffle Developers. All rights reserved.
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file MagicSet.cpp
12  *
13  * Define classes and functionality related to the magic set transformation.
14  *
15  ***********************************************************************/
16 
17 #include "ast/transform/MagicSet.h"
18 #include "Global.h"
19 #include "ast/Aggregator.h"
20 #include "ast/Attribute.h"
21 #include "ast/BinaryConstraint.h"
22 #include "ast/Constant.h"
23 #include "ast/Directive.h"
24 #include "ast/Functor.h"
25 #include "ast/Node.h"
26 #include "ast/NumericConstant.h"
27 #include "ast/Program.h"
28 #include "ast/QualifiedName.h"
29 #include "ast/RecordInit.h"
30 #include "ast/Relation.h"
31 #include "ast/StringConstant.h"
32 #include "ast/TranslationUnit.h"
33 #include "ast/UnnamedVariable.h"
34 #include "ast/analysis/IOType.h"
38 #include "ast/analysis/SCCGraph.h"
40 #include "ast/utility/NodeMapper.h"
41 #include "ast/utility/Utils.h"
42 #include "ast/utility/Visitor.h"
43 #include "parser/SrcLocation.h"
45 #include "souffle/RamTypes.h"
49 #include <algorithm>
50 #include <optional>
51 #include <utility>
52 
53 namespace souffle::ast::transform {
58 
63 
64 std::set<QualifiedName> MagicSetTransformer::getTriviallyIgnoredRelations(const TranslationUnit& tu) {
65  const auto& program = tu.getProgram();
66  const auto& ioTypes = *tu.getAnalysis<analysis::IOTypeAnalysis>();
67  std::set<QualifiedName> triviallyIgnoredRelations;
68 
69  // - Any relations known in constant time (IDB relations)
70  for (auto* rel : program.getRelations()) {
71  // Input relations
72  if (ioTypes.isInput(rel)) {
73  triviallyIgnoredRelations.insert(rel->getQualifiedName());
74  continue;
75  }
76 
77  // Any relations not dependent on any atoms
78  bool hasRules = false;
79  for (const auto* clause : getClauses(program, rel->getQualifiedName())) {
80  visitDepthFirst(clause->getBodyLiterals(), [&](const Atom& /* atom */) { hasRules = true; });
81  }
82  if (!hasRules) {
83  triviallyIgnoredRelations.insert(rel->getQualifiedName());
84  }
85  }
86 
87  return triviallyIgnoredRelations;
88 }
89 
90 std::set<QualifiedName> MagicSetTransformer::getWeaklyIgnoredRelations(const TranslationUnit& tu) {
91  const auto& program = tu.getProgram();
92  const auto& precedenceGraph = tu.getAnalysis<analysis::PrecedenceGraphAnalysis>()->graph();
93  const auto& polyAnalysis = *tu.getAnalysis<analysis::PolymorphicObjectsAnalysis>();
94  std::set<QualifiedName> weaklyIgnoredRelations;
95 
96  // - Any relations not specified to magic-set
97  std::vector<QualifiedName> specifiedRelations;
98 
99  // Pick up specified relations from config
100  std::vector<std::string> configRels = splitString(Global::config().get("magic-transform"), ',');
101  for (const auto& relStr : configRels) {
102  std::vector<std::string> qualifiers = splitString(relStr, '.');
103  specifiedRelations.push_back(QualifiedName(qualifiers));
104  }
105 
106  // Pick up specified relations from relation tags
107  for (const auto* rel : program.getRelations()) {
108  if (rel->hasQualifier(RelationQualifier::MAGIC)) {
109  specifiedRelations.push_back(rel->getQualifiedName());
110  }
111  }
112 
113  // Get the complement if not everything is magic'd
114  if (!contains(configRels, "*")) {
115  for (const Relation* rel : program.getRelations()) {
116  if (!contains(specifiedRelations, rel->getQualifiedName())) {
117  weaklyIgnoredRelations.insert(rel->getQualifiedName());
118  }
119  }
120  }
121 
122  // - Add trivially computable relations
123  for (const auto& relName : getTriviallyIgnoredRelations(tu)) {
124  weaklyIgnoredRelations.insert(relName);
125  }
126 
127  // - Any relation with a neglabel
128  visitDepthFirst(program, [&](const Atom& atom) {
129  const auto& qualifiers = atom.getQualifiedName().getQualifiers();
130  if (!qualifiers.empty() && qualifiers[0] == "@neglabel") {
131  weaklyIgnoredRelations.insert(atom.getQualifiedName());
132  }
133  });
134 
135  // - Any relation with a clause containing float-related binary constraints
136  const std::set<BinaryConstraintOp> floatOps(
139  for (const auto* clause : program.getClauses()) {
140  visitDepthFirst(*clause, [&](const BinaryConstraint& bc) {
141  if (contains(floatOps, polyAnalysis.getOverloadedOperator(&bc))) {
142  weaklyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
143  }
144  });
145  }
146 
147  // - Any relation with a clause containing order-dependent functors
148  const std::set<FunctorOp> orderDepFuncOps(
150  for (const auto* clause : program.getClauses()) {
151  visitDepthFirst(*clause, [&](const IntrinsicFunctor& functor) {
152  if (contains(orderDepFuncOps, polyAnalysis.getOverloadedFunctionOp(&functor))) {
153  weaklyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
154  }
155  });
156  }
157 
158  // - Any eqrel relation
159  for (auto* rel : program.getRelations()) {
160  if (rel->getRepresentation() == RelationRepresentation::EQREL) {
161  weaklyIgnoredRelations.insert(rel->getQualifiedName());
162  }
163  }
164 
165  // - Any relation with execution plans
166  for (auto* clause : program.getClauses()) {
167  if (clause->getExecutionPlan() != nullptr) {
168  weaklyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
169  }
170  }
171 
172  // - Any atom appearing in a clause containing a counter
173  for (auto* clause : program.getClauses()) {
174  bool containsCounter = false;
175  visitDepthFirst(*clause, [&](const Counter& /* counter */) { containsCounter = true; });
176  if (containsCounter) {
177  visitDepthFirst(*clause,
178  [&](const Atom& atom) { weaklyIgnoredRelations.insert(atom.getQualifiedName()); });
179  }
180  }
181 
182  // - Deal with strongly ignored relations
183  const auto& stronglyIgnoredRelations = getStronglyIgnoredRelations(tu);
184 
185  // Add them in directly
186  for (const auto& relName : stronglyIgnoredRelations) {
187  weaklyIgnoredRelations.insert(relName);
188  }
189 
190  // Add in any atoms whose magic rules might cause a need for neglabelling
191  // - Essentially, suppose R is strongly-ignored and A depends on R. Then, we must weakly ignore
192  // any relation that appears after A in any clause, otherwise a magic-set might be created that
193  // requires R to be neglabelled.
194  for (const auto& relName : stronglyIgnoredRelations) {
195  precedenceGraph.visitDepthFirst(getRelation(program, relName), [&](const auto* dependentRel) {
196  const auto& depName = dependentRel->getQualifiedName();
197  for (const auto* clause : program.getClauses()) {
198  const auto& atoms = getBodyLiterals<Atom>(*clause);
199  bool startIgnoring = false;
200  for (const auto& atom : atoms) {
201  startIgnoring |= (atom->getQualifiedName() == depName);
202  if (startIgnoring) {
203  weaklyIgnoredRelations.insert(atom->getQualifiedName());
204  }
205  }
206  }
207  });
208  }
209 
210  return weaklyIgnoredRelations;
211 }
212 
213 std::set<QualifiedName> MagicSetTransformer::getStronglyIgnoredRelations(const TranslationUnit& tu) {
214  const auto& program = tu.getProgram();
215  const auto& relDetail = *tu.getAnalysis<analysis::RelationDetailCacheAnalysis>();
216  const auto& precedenceGraph = tu.getAnalysis<analysis::PrecedenceGraphAnalysis>()->graph();
217  std::set<QualifiedName> stronglyIgnoredRelations;
218 
219  // - Any atom appearing at the head of a clause containing a counter
220  for (const auto* clause : program.getClauses()) {
221  bool containsCounter = false;
222  visitDepthFirst(*clause, [&](const Counter& /* counter */) { containsCounter = true; });
223  if (containsCounter) {
224  stronglyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
225  }
226  }
227 
228  bool fixpointReached = false;
229  while (!fixpointReached) {
230  fixpointReached = true;
231  // - To prevent poslabelling issues, all dependent strata should also be strongly ignored
232  std::set<QualifiedName> dependentRelations;
233  for (const auto& relName : stronglyIgnoredRelations) {
234  precedenceGraph.visitDepthFirst(getRelation(program, relName), [&](const auto* dependentRel) {
235  dependentRelations.insert(dependentRel->getQualifiedName());
236  });
237  }
238  for (const auto& depRel : dependentRelations) {
239  if (!contains(stronglyIgnoredRelations, depRel)) {
240  fixpointReached = false;
241  stronglyIgnoredRelations.insert(depRel);
242  }
243  }
244 
245  // - Since we can't duplicate the rules, nothing should be labelled in the bodies as well
246  std::set<QualifiedName> bodyRelations;
247  for (const auto& relName : stronglyIgnoredRelations) {
248  for (const auto* clause : relDetail.getClauses(relName)) {
250  *clause, [&](const Atom& atom) { bodyRelations.insert(atom.getQualifiedName()); });
251  }
252  }
253  for (const auto& bodyRel : bodyRelations) {
254  if (!contains(stronglyIgnoredRelations, bodyRel)) {
255  fixpointReached = false;
256  stronglyIgnoredRelations.insert(bodyRel);
257  }
258  }
259  }
260 
261  return stronglyIgnoredRelations;
262 }
263 
264 std::set<QualifiedName> MagicSetTransformer::getRelationsToNotLabel(const TranslationUnit& tu) {
265  std::set<QualifiedName> result;
266  for (const auto& name : getTriviallyIgnoredRelations(tu)) {
267  result.insert(name);
268  }
269  for (const auto& name : getStronglyIgnoredRelations(tu)) {
270  result.insert(name);
271  }
272  return result;
273 }
274 
275 bool MagicSetTransformer::shouldRun(const TranslationUnit& tu) {
276  const Program& program = tu.getProgram();
277  if (Global::config().has("magic-transform")) return true;
278  for (const auto* rel : program.getRelations()) {
279  if (rel->hasQualifier(RelationQualifier::MAGIC)) return true;
280  }
281  return false;
282 }
283 
284 bool NormaliseDatabaseTransformer::transform(TranslationUnit& translationUnit) {
285  bool changed = false;
286 
287  /** (1) Partition input and output relations */
288  changed |= partitionIO(translationUnit);
289  if (changed) translationUnit.invalidateAnalyses();
290 
291  /** (2) Separate the IDB from the EDB */
292  changed |= extractIDB(translationUnit);
293  if (changed) translationUnit.invalidateAnalyses();
294 
295  /** (3) Normalise arguments within each clause */
296  changed |= normaliseArguments(translationUnit);
297  if (changed) translationUnit.invalidateAnalyses();
298 
299  /** (4) Querify output relations */
300  changed |= querifyOutputRelations(translationUnit);
301  if (changed) translationUnit.invalidateAnalyses();
302 
303  return changed;
304 }
305 
306 bool NormaliseDatabaseTransformer::partitionIO(TranslationUnit& translationUnit) {
307  Program& program = translationUnit.getProgram();
308  const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
309 
310  // Get all relations that are both input and output
311  std::set<QualifiedName> relationsToSplit;
312  for (auto* rel : 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
379  auto isStrictlyEDB = [&](const Relation* rel) {
380  bool hasRules = false;
381  for (const auto* clause : 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) {
442  bool strictlyOutput = true;
443  size_t ruleCount = 0;
444 
445  for (const auto* clause : 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
516  struct argument_normaliser : public NodeMapper {
517  std::set<Own<BinaryConstraint>>& constraints;
518  int& changeCount;
519 
520  argument_normaliser(std::set<Own<BinaryConstraint>>& constraints, int& changeCount)
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 
612 QualifiedName AdornDatabaseTransformer::getAdornmentID(
613  const QualifiedName& relName, const std::string& adornmentMarker) {
614  if (adornmentMarker == "") return relName;
615  QualifiedName adornmentID(relName);
616  std::stringstream adornmentMarkerRepr;
617  adornmentMarkerRepr << "{" << adornmentMarker << "}";
618  adornmentID.append(adornmentMarkerRepr.str());
619  return adornmentID;
620 }
621 
622 Own<Clause> AdornDatabaseTransformer::adornClause(const Clause* clause, const std::string& adornmentMarker) {
623  const auto& relName = clause->getHead()->getQualifiedName();
624  const auto& headArgs = clause->getHead()->getArguments();
625  BindingStore variableBindings(clause);
626 
627  /* Note that variables can be bound through:
628  * (1) an appearance in a body atom (strong)
629  * (2) an appearance in a bound field of the head atom (weak)
630  * (3) equality with a fully bound functor (via dependency analysis)
631  *
632  * When computing (3), appearances (1) and (2) must be separated to maintain the termination semantics of
633  * the original program. Functor variables are not considered bound if they are only bound via the head.
634  *
635  * Justification: Suppose a new variable Y is marked as bound because of its appearance in a functor
636  * Y=X+1, and X was already found to be bound:
637  * (1) If X was bound through a body atom, then the behaviour of typical magic-set is exhibited, where
638  * the magic-set of Y is bounded by the values that X can take, which is bounded by induction.
639  * (2) If X was bound only through the head atom, then Y is only fixed to an appearance in a magic-atom.
640  * In the presence of recursion, this can potentially lead to an infinitely-sized magic-set for an atom.
641  *
642  * Therefore, bound head atom vars should be marked as weakly bound.
643  */
644  for (size_t i = 0; i < adornmentMarker.length(); i++) {
645  const auto* var = dynamic_cast<ast::Variable*>(headArgs[i]);
646  assert(var != nullptr && "expected only variables in head");
647  if (adornmentMarker[i] == 'b') {
648  variableBindings.bindVariableWeakly(var->getName());
649  }
650  }
651 
652  // Create the adorned clause with an empty body
653  auto adornedClause = mk<Clause>();
654 
655  // Copy over plans if needed
656  if (clause->getExecutionPlan() != nullptr) {
657  assert(contains(weaklyIgnoredRelations, clause->getHead()->getQualifiedName()) &&
658  "clauses with plans should be ignored");
659  adornedClause->setExecutionPlan(souffle::clone(clause->getExecutionPlan()));
660  }
661 
662  // Create the head atom
663  auto adornedHeadAtom = mk<Atom>(getAdornmentID(relName, adornmentMarker));
664  assert((adornmentMarker == "" || headArgs.size() == adornmentMarker.length()) &&
665  "adornment marker should correspond to head atom variables");
666  for (const auto* arg : headArgs) {
667  const auto* var = dynamic_cast<const ast::Variable*>(arg);
668  assert(var != nullptr && "expected only variables in head");
669  adornedHeadAtom->addArgument(souffle::clone(var));
670  }
671  adornedClause->setHead(std::move(adornedHeadAtom));
672 
673  // Add in adorned body literals
674  std::vector<Own<Literal>> adornedBodyLiterals;
675  for (const auto* lit : clause->getBodyLiterals()) {
676  if (const auto* negation = dynamic_cast<const Negation*>(lit)) {
677  // Negated atoms should not be adorned, but their clauses should be anyway
678  const auto negatedAtomName = negation->getAtom()->getQualifiedName();
679  assert(contains(weaklyIgnoredRelations, negatedAtomName) &&
680  "negated atoms should not be adorned");
681  queueAdornment(negatedAtomName, "");
682  }
683 
684  if (!isA<Atom>(lit)) {
685  // Non-atoms are added directly
686  adornedBodyLiterals.push_back(souffle::clone(lit));
687  continue;
688  }
689 
690  const auto* atom = dynamic_cast<const Atom*>(lit);
691  assert(atom != nullptr && "expected atom");
692 
693  // Form the appropriate adornment marker
694  std::stringstream atomAdornment;
695  if (!contains(weaklyIgnoredRelations, atom->getQualifiedName())) {
696  for (const auto* arg : atom->getArguments()) {
697  const auto* var = dynamic_cast<const ast::Variable*>(arg);
698  assert(var != nullptr && "expected only variables in atom");
699  atomAdornment << (variableBindings.isBound(var->getName()) ? "b" : "f");
700  }
701  }
702  auto currAtomAdornmentID = getAdornmentID(atom->getQualifiedName(), atomAdornment.str());
703 
704  // Add to the ToDo queue if needed
705  queueAdornment(atom->getQualifiedName(), atomAdornment.str());
706 
707  // Add the adorned version to the clause
708  auto adornedBodyAtom = souffle::clone(atom);
709  adornedBodyAtom->setQualifiedName(currAtomAdornmentID);
710  adornedBodyLiterals.push_back(std::move(adornedBodyAtom));
711 
712  // All arguments are now bound
713  for (const auto* arg : atom->getArguments()) {
714  const auto* var = dynamic_cast<const ast::Variable*>(arg);
715  assert(var != nullptr && "expected only variables in atom");
716  variableBindings.bindVariableStrongly(var->getName());
717  }
718  }
719  adornedClause->setBodyLiterals(std::move(adornedBodyLiterals));
720 
721  return adornedClause;
722 }
723 
724 bool AdornDatabaseTransformer::transform(TranslationUnit& translationUnit) {
725  Program& program = translationUnit.getProgram();
726  const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
727  weaklyIgnoredRelations = getWeaklyIgnoredRelations(translationUnit);
728 
729  // Output relations trigger the adornment process
730  for (const auto* rel : program.getRelations()) {
731  if (ioTypes.isOutput(rel) || ioTypes.isPrintSize(rel)) {
732  queueAdornment(rel->getQualifiedName(), "");
733  }
734  }
735 
736  // Keep going while there's things to adorn
737  while (hasAdornmentToProcess()) {
738  // Pop off the next head adornment to do
739  const auto& [relName, adornmentMarker] = nextAdornmentToProcess();
740 
741  // Add the adorned relation if needed
742  if (adornmentMarker != "") {
743  const auto* rel = getRelation(program, relName);
744  assert(rel != nullptr && "relation does not exist");
745 
746  auto adornedRelation = mk<Relation>(getAdornmentID(relName, adornmentMarker));
747  for (const auto* attr : rel->getAttributes()) {
748  adornedRelation->addAttribute(souffle::clone(attr));
749  }
750  program.addRelation(std::move(adornedRelation));
751  }
752 
753  // Adorn every clause correspondingly
754  for (const auto* clause : getClauses(program, relName)) {
755  if (adornmentMarker == "") {
756  redundantClauses.push_back(souffle::clone(clause));
757  }
758  auto adornedClause = adornClause(clause, adornmentMarker);
759  adornedClauses.push_back(std::move(adornedClause));
760  }
761  }
762 
763  // Swap over the redundant clauses with the adorned clauses
764  for (const auto& clause : redundantClauses) {
765  program.removeClause(clause.get());
766  }
767 
768  for (auto& clause : adornedClauses) {
769  program.addClause(souffle::clone(clause));
770  }
771 
772  return !adornedClauses.empty() || !redundantClauses.empty();
773 }
774 
775 QualifiedName NegativeLabellingTransformer::getNegativeLabel(const QualifiedName& name) {
776  QualifiedName newName(name);
777  newName.prepend("@neglabel");
778  return newName;
779 }
780 
781 QualifiedName PositiveLabellingTransformer::getPositiveLabel(const QualifiedName& name, size_t count) {
782  std::stringstream label;
783  label << "@poscopy_" << count;
784  QualifiedName labelledName(name);
785  labelledName.prepend(label.str());
786  return labelledName;
787 }
788 
789 bool LabelDatabaseTransformer::isNegativelyLabelled(const QualifiedName& name) {
790  auto qualifiers = name.getQualifiers();
791  assert(!qualifiers.empty() && "unexpected empty qualifier list");
792  return qualifiers[0] == "@neglabel";
793 }
794 
795 bool NegativeLabellingTransformer::transform(TranslationUnit& translationUnit) {
796  const auto& sccGraph = *translationUnit.getAnalysis<analysis::SCCGraphAnalysis>();
797  Program& program = translationUnit.getProgram();
798 
799  std::set<QualifiedName> relationsToLabel;
800  std::set<Own<Clause>> clausesToAdd;
801  const auto& relationsToNotLabel = getRelationsToNotLabel(translationUnit);
802 
803  // Negatively label all relations that might affect stratification after MST
804  // - Negated relations
805  // - Relations that appear in aggregators
806  visitDepthFirst(program, [&](const Negation& neg) {
807  auto* atom = neg.getAtom();
808  auto relName = atom->getQualifiedName();
809  if (contains(relationsToNotLabel, relName)) return;
810  atom->setQualifiedName(getNegativeLabel(relName));
811  relationsToLabel.insert(relName);
812  });
813  visitDepthFirst(program, [&](const Aggregator& aggr) {
814  visitDepthFirst(aggr, [&](const Atom& atom) {
815  auto relName = atom.getQualifiedName();
816  if (contains(relationsToNotLabel, relName)) return;
817  const_cast<Atom&>(atom).setQualifiedName(getNegativeLabel(relName));
818  relationsToLabel.insert(relName);
819  });
820  });
821 
822  // Copy over the rules for labelled relations one stratum at a time
823  for (size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
824  // Check which relations to label in this stratum
825  const auto& stratumRels = sccGraph.getInternalRelations(stratum);
826  std::map<QualifiedName, QualifiedName> newSccFriendNames;
827  for (const auto* rel : stratumRels) {
828  auto relName = rel->getQualifiedName();
829  if (contains(relationsToNotLabel, relName)) continue;
830  relationsToLabel.insert(relName);
831  newSccFriendNames[relName] = getNegativeLabel(relName);
832  }
833 
834  // Negatively label the relations in a new copy of this stratum
835  for (const auto* rel : stratumRels) {
836  if (contains(relationsToNotLabel, rel->getQualifiedName())) continue;
837  for (auto* clause : getClauses(program, rel->getQualifiedName())) {
838  auto neggedClause = souffle::clone(clause);
839  renameAtoms(*neggedClause, newSccFriendNames);
840  clausesToAdd.insert(std::move(neggedClause));
841  }
842  }
843  }
844 
845  // Add in all the relations that were labelled
846  for (const auto& relName : relationsToLabel) {
847  const auto* originalRel = getRelation(program, relName);
848  assert(originalRel != nullptr && "unlabelled relation does not exist");
849  auto labelledRelation = souffle::clone(originalRel);
850  labelledRelation->setQualifiedName(getNegativeLabel(relName));
851  program.addRelation(std::move(labelledRelation));
852  }
853 
854  // Add in all the negged clauses
855  for (const auto& clause : clausesToAdd) {
856  program.addClause(souffle::clone(clause));
857  }
858 
859  return !relationsToLabel.empty();
860 }
861 
862 bool PositiveLabellingTransformer::transform(TranslationUnit& translationUnit) {
863  Program& program = translationUnit.getProgram();
864  const auto& sccGraph = *translationUnit.getAnalysis<analysis::SCCGraphAnalysis>();
865  const auto& precedenceGraph = translationUnit.getAnalysis<analysis::PrecedenceGraphAnalysis>()->graph();
866  const auto& relationsToNotLabel = getRelationsToNotLabel(translationUnit);
867 
868  // Partition the strata into neglabelled and regular
869  std::set<size_t> neglabelledStrata;
870  std::map<size_t, size_t> originalStrataCopyCount;
871  for (size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
872  size_t numNeggedRelations = 0;
873  const auto& stratumRels = sccGraph.getInternalRelations(stratum);
874 
875  // Count how many relations in this node are neglabelled
876  for (const auto* rel : stratumRels) {
877  if (isNegativelyLabelled(rel->getQualifiedName())) {
878  numNeggedRelations++;
879  }
880  }
881  assert((numNeggedRelations == 0 || numNeggedRelations == stratumRels.size()) &&
882  "stratum cannot contain a mix of neglabelled and unlabelled relations");
883 
884  if (numNeggedRelations > 0) {
885  // This is a neglabelled stratum that will not be copied
886  neglabelledStrata.insert(stratum);
887  } else {
888  // This is a regular stratum that may be copied
889  originalStrataCopyCount[stratum] = 0;
890  }
891  }
892 
893  // Keep track of strata that depend on each stratum
894  // e.g. T in dependentStrata[S] iff a relation in T depends on a relation in S
895  std::map<size_t, std::set<size_t>> dependentStrata;
896  for (size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
897  dependentStrata[stratum] = std::set<size_t>();
898  }
899  for (const auto* rel : program.getRelations()) {
900  size_t stratum = sccGraph.getSCC(rel);
901  precedenceGraph.visitDepthFirst(rel, [&](const auto* dependentRel) {
902  dependentStrata[stratum].insert(sccGraph.getSCC(dependentRel));
903  });
904  }
905 
906  // Label the positive derived literals in the clauses of neglabelled relations
907  // Need a new copy of those relations up to that point for each
908  for (size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
909  if (!contains(neglabelledStrata, stratum)) continue;
910 
911  // Rename in the current stratum
912  for (const auto* rel : sccGraph.getInternalRelations(stratum)) {
913  assert(isNegativelyLabelled(rel->getQualifiedName()) &&
914  "should only be looking at neglabelled strata");
915  const auto& clauses = getClauses(program, *rel);
916  std::set<QualifiedName> relsToCopy;
917 
918  // Get the unignored unlabelled relations appearing in the rules
919  for (const auto* clause : clauses) {
920  visitDepthFirst(*clause, [&](const Atom& atom) {
921  const auto& name = atom.getQualifiedName();
922  if (!contains(relationsToNotLabel, name) && !isNegativelyLabelled(name)) {
923  relsToCopy.insert(name);
924  }
925  });
926  }
927 
928  // Positively label them
929  for (auto* clause : clauses) {
930  std::map<QualifiedName, QualifiedName> labelledNames;
931  for (const auto& relName : relsToCopy) {
932  size_t relStratum = sccGraph.getSCC(getRelation(program, relName));
933  size_t copyCount = originalStrataCopyCount.at(relStratum) + 1;
934  labelledNames[relName] = getPositiveLabel(relName, copyCount);
935  }
936  renameAtoms(*clause, labelledNames);
937  }
938  }
939 
940  // Create the rules (from all previous strata) for the newly positive labelled literals
941  for (int preStratum = stratum - 1; preStratum >= 0; preStratum--) {
942  if (contains(neglabelledStrata, preStratum)) continue;
943  if (!contains(dependentStrata[preStratum], stratum)) continue;
944 
945  for (const auto* rel : sccGraph.getInternalRelations(preStratum)) {
946  if (contains(relationsToNotLabel, rel->getQualifiedName())) continue;
947 
948  for (const auto* clause : getClauses(program, rel->getQualifiedName())) {
949  // Grab the new names for all unignored unlabelled positive atoms
950  std::map<QualifiedName, QualifiedName> labelledNames;
951  visitDepthFirst(*clause, [&](const Atom& atom) {
952  const auto& relName = atom.getQualifiedName();
953  if (contains(relationsToNotLabel, relName) || isNegativelyLabelled(relName)) return;
954  size_t relStratum = sccGraph.getSCC(getRelation(program, relName));
955  size_t copyCount = originalStrataCopyCount.at(relStratum) + 1;
956  labelledNames[relName] = getPositiveLabel(relName, copyCount);
957  });
958 
959  // Rename atoms accordingly
960  auto labelledClause = souffle::clone(clause);
961  renameAtoms(*labelledClause, labelledNames);
962  program.addClause(std::move(labelledClause));
963  }
964  }
965 
966  originalStrataCopyCount[preStratum]++;
967  }
968  }
969 
970  // Add each copy for each relation in
971  bool changed = false;
972  for (const auto& [stratum, numCopies] : originalStrataCopyCount) {
973  const auto& stratumRels = sccGraph.getInternalRelations(stratum);
974  for (size_t copy = 0; copy < numCopies; copy++) {
975  for (auto* rel : stratumRels) {
976  auto newRelation = souffle::clone(rel);
977  newRelation->setQualifiedName(getPositiveLabel(newRelation->getQualifiedName(), copy + 1));
978  program.addRelation(std::move(newRelation));
979  changed = true;
980  }
981  }
982  }
983  return changed;
984 }
985 
986 bool MagicSetCoreTransformer::isAdorned(const QualifiedName& name) {
987  // Grab the final qualifier - this is where the adornment is if it exists
988  auto qualifiers = name.getQualifiers();
989  assert(!qualifiers.empty() && "unexpected empty qualifier list");
990  auto finalQualifier = qualifiers[qualifiers.size() - 1];
991  assert(finalQualifier.length() > 0 && "unexpected empty qualifier");
992 
993  // Pattern: {[bf]*}
994  if (finalQualifier[0] == '{' && finalQualifier[finalQualifier.length() - 1] == '}') {
995  for (size_t i = 1; i < finalQualifier.length() - 1; i++) {
996  char curBindingType = finalQualifier[i];
997  if (curBindingType != 'b' && curBindingType != 'f') {
998  return false;
999  }
1000  }
1001  return true;
1002  }
1003  return false;
1004 }
1005 
1006 std::string MagicSetCoreTransformer::getAdornment(const QualifiedName& name) {
1007  assert(isAdorned(name) && "relation not adorned");
1008  auto qualifiers = name.getQualifiers();
1009  auto finalQualifier = qualifiers[qualifiers.size() - 1];
1010  std::stringstream binding;
1011  for (size_t i = 1; i < finalQualifier.length() - 1; i++) {
1012  binding << finalQualifier[i];
1013  }
1014  return binding.str();
1015 }
1016 
1017 QualifiedName MagicSetCoreTransformer::getMagicName(const QualifiedName& name) {
1018  assert(isAdorned(name) && "cannot magify unadorned predicates");
1019  QualifiedName magicRelName(name);
1020  magicRelName.prepend("@magic");
1021  return magicRelName;
1022 }
1024 Own<Atom> MagicSetCoreTransformer::createMagicAtom(const Atom* atom) {
1025  auto origRelName = atom->getQualifiedName();
1026  auto args = atom->getArguments();
1027 
1028  auto magicAtom = mk<Atom>(getMagicName(origRelName));
1029 
1030  auto adornmentMarker = getAdornment(origRelName);
1031  for (size_t i = 0; i < args.size(); i++) {
1032  if (adornmentMarker[i] == 'b') {
1033  magicAtom->addArgument(souffle::clone(args[i]));
1034  }
1035  }
1036 
1037  return magicAtom;
1038 }
1039 
1040 void MagicSetCoreTransformer::addRelevantVariables(
1041  std::set<std::string>& variables, const std::vector<const BinaryConstraint*> eqConstraints) {
1042  // Helper method to check if all variables in an argument are bound
1043  auto isFullyBound = [&](const Argument* arg) {
1044  bool fullyBound = true;
1046  *arg, [&](const ast::Variable& var) { fullyBound &= contains(variables, var.getName()); });
1047  return fullyBound;
1048  };
1049 
1050  // Helper method to add all newly relevant variables given a lhs = rhs constraint
1051  auto addLocallyRelevantVariables = [&](const Argument* lhs, const Argument* rhs) {
1052  const auto* lhsVar = dynamic_cast<const ast::Variable*>(lhs);
1053  if (lhsVar == nullptr) return true;
1054 
1055  // if the rhs is fully bound, lhs is now bound
1056  if (!contains(variables, lhsVar->getName())) {
1057  if (isFullyBound(rhs)) {
1058  variables.insert(lhsVar->getName());
1059  return false;
1060  } else {
1061  return true;
1062  }
1063  }
1064 
1065  // if the rhs is a record, and lhs is a bound var, then all rhs vars are bound
1066  bool fixpointReached = true;
1067  if (const auto* rhsRec = dynamic_cast<const RecordInit*>(rhs)) {
1068  for (const auto* arg : rhsRec->getArguments()) {
1069  const auto* subVar = dynamic_cast<const ast::Variable*>(arg);
1070  assert(subVar != nullptr && "expected only variable arguments");
1071  if (!contains(variables, subVar->getName())) {
1072  fixpointReached = false;
1073  variables.insert(subVar->getName());
1074  }
1075  }
1076  }
1077 
1078  return fixpointReached;
1079  };
1080 
1081  // Keep adding in relevant variables until we reach a fixpoint
1082  bool fixpointReached = false;
1083  while (!fixpointReached) {
1084  fixpointReached = true;
1085  for (const auto* eqConstraint : eqConstraints) {
1086  assert(isEqConstraint(eqConstraint->getBaseOperator()) && "expected only eq constraints");
1087  fixpointReached &= addLocallyRelevantVariables(eqConstraint->getLHS(), eqConstraint->getRHS());
1088  fixpointReached &= addLocallyRelevantVariables(eqConstraint->getRHS(), eqConstraint->getLHS());
1089  }
1090  }
1091 }
1092 
1093 Own<Clause> MagicSetCoreTransformer::createMagicClause(const Atom* atom,
1094  const std::vector<Own<Atom>>& constrainingAtoms,
1095  const std::vector<const BinaryConstraint*> eqConstraints) {
1096  auto magicHead = createMagicAtom(atom);
1097  auto magicClause = mk<Clause>();
1098 
1099  // Add in all constraining atoms
1100  for (const auto& bindingAtom : constrainingAtoms) {
1101  magicClause->addToBody(souffle::clone(bindingAtom));
1102  }
1103 
1104  // Get the set of all variables that will be relevant to the magic clause
1105  std::set<std::string> relevantVariables;
1107  constrainingAtoms, [&](const ast::Variable& var) { relevantVariables.insert(var.getName()); });
1108  visitDepthFirst(*magicHead, [&](const ast::Variable& var) { relevantVariables.insert(var.getName()); });
1109  addRelevantVariables(relevantVariables, eqConstraints);
1110 
1111  // Add in all eq constraints containing ONLY relevant variables
1112  for (const auto* eqConstraint : eqConstraints) {
1113  bool addConstraint = true;
1114  visitDepthFirst(*eqConstraint, [&](const ast::Variable& var) {
1115  if (!contains(relevantVariables, var.getName())) {
1116  addConstraint = false;
1117  }
1118  });
1119 
1120  if (addConstraint) magicClause->addToBody(souffle::clone(eqConstraint));
1121  }
1122 
1123  magicClause->setHead(std::move(magicHead));
1124  return magicClause;
1125 }
1126 
1127 std::vector<const BinaryConstraint*> MagicSetCoreTransformer::getBindingEqualityConstraints(
1128  const Clause* clause) {
1129  std::vector<const BinaryConstraint*> equalityConstraints;
1130  for (const auto* lit : clause->getBodyLiterals()) {
1131  const auto* bc = dynamic_cast<const BinaryConstraint*>(lit);
1132  if (bc == nullptr || !isEqConstraint(bc->getBaseOperator())) continue;
1133  if (isA<ast::Variable>(bc->getLHS()) || isA<Constant>(bc->getRHS())) {
1134  bool containsAggrs = false;
1135  visitDepthFirst(*bc, [&](const Aggregator& /* aggr */) { containsAggrs = true; });
1136  if (!containsAggrs) {
1137  equalityConstraints.push_back(bc);
1138  }
1139  }
1140  }
1141  return equalityConstraints;
1142 }
1143 
1144 bool MagicSetCoreTransformer::transform(TranslationUnit& translationUnit) {
1145  Program& program = translationUnit.getProgram();
1146  std::set<Own<Clause>> clausesToRemove;
1147  std::set<Own<Clause>> clausesToAdd;
1148 
1149  /** Perform the Magic Set Transformation */
1150  for (const auto* clause : program.getClauses()) {
1151  clausesToRemove.insert(souffle::clone(clause));
1152 
1153  const auto* head = clause->getHead();
1154  auto relName = head->getQualifiedName();
1155 
1156  // (1) Add the refined clause
1157  if (!isAdorned(relName)) {
1158  // Unadorned relations need not be refined, as every possible tuple is relevant
1159  clausesToAdd.insert(souffle::clone(clause));
1160  } else {
1161  // Refine the clause with a prepended magic atom
1162  auto magicAtom = createMagicAtom(head);
1163  auto refinedClause = mk<Clause>();
1164  refinedClause->setHead(souffle::clone(head));
1165  refinedClause->addToBody(souffle::clone(magicAtom));
1166  for (auto* literal : clause->getBodyLiterals()) {
1167  refinedClause->addToBody(souffle::clone(literal));
1168  }
1169  clausesToAdd.insert(std::move(refinedClause));
1170  }
1171 
1172  // (2) Add the associated magic rules
1173  std::vector<const BinaryConstraint*> eqConstraints = getBindingEqualityConstraints(clause);
1174  std::vector<Own<Atom>> atomsToTheLeft;
1175  if (isAdorned(relName)) {
1176  // Add the specialising head atom
1177  // Output relations are not specialised, and so the head will not contribute to specialisation
1178  atomsToTheLeft.push_back(createMagicAtom(clause->getHead()));
1179  }
1180  for (const auto* lit : clause->getBodyLiterals()) {
1181  const auto* atom = dynamic_cast<const Atom*>(lit);
1182  if (atom == nullptr) continue;
1183  if (!isAdorned(atom->getQualifiedName())) {
1184  atomsToTheLeft.push_back(souffle::clone(atom));
1185  continue;
1186  }
1187 
1188  // Need to create a magic rule
1189  auto magicClause = createMagicClause(atom, atomsToTheLeft, eqConstraints);
1190  atomsToTheLeft.push_back(souffle::clone(atom));
1191  clausesToAdd.insert(std::move(magicClause));
1192  }
1193  }
1194 
1195  for (auto& clause : clausesToAdd) {
1196  program.addClause(souffle::clone(clause));
1197  }
1198  for (const auto& clause : clausesToRemove) {
1199  program.removeClause(clause.get());
1200  }
1201 
1202  // Add in the magic relations
1203  bool changed = false;
1204  for (const auto* rel : program.getRelations()) {
1205  const auto& origName = rel->getQualifiedName();
1206  if (!isAdorned(origName)) continue;
1207  auto magicRelation = mk<Relation>(getMagicName(origName));
1208  auto attributes = getRelation(program, origName)->getAttributes();
1209  auto adornmentMarker = getAdornment(origName);
1210  for (size_t i = 0; i < attributes.size(); i++) {
1211  if (adornmentMarker[i] == 'b') {
1212  magicRelation->addAttribute(souffle::clone(attributes[i]));
1213  }
1214  }
1215  changed = true;
1216  program.addRelation(std::move(magicRelation));
1217  }
1218  return changed;
1219 }
1220 
1221 } // namespace souffle::ast::transform
souffle::FunctorOp::DIV
@ DIV
BinaryConstraintOps.h
souffle::FunctorOp::FDIV
@ FDIV
souffle::ast::QualifiedName::getQualifiers
const std::vector< std::string > & getQualifiers() const
get qualifiers
Definition: QualifiedName.h:66
TranslationUnit.h
Functor.h
souffle::ast::Atom::getArguments
std::vector< Argument * > getArguments() const
Return arguments.
Definition: Atom.h:77
UnnamedVariable.h
IOType.h
souffle::ast::analysis::IOTypeAnalysis
Definition: IOType.h:39
souffle::BinaryConstraintOp::FGE
@ FGE
souffle::BinaryConstraintOp::FNE
@ FNE
souffle::RelationRepresentation::EQREL
@ EQREL
Directive.h
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
SrcLocation.h
souffle::ast::Clause::getExecutionPlan
const ExecutionPlan * getExecutionPlan() const
Obtains the execution plan associated to this clause or null if there is none.
Definition: Clause.h:84
souffle::ast::TranslationUnit::invalidateAnalyses
void invalidateAnalyses() const
Destroy all cached analyses of translation unit.
Definition: TranslationUnit.h:95
RelationDetailCache.h
souffle::ast::analysis::PolymorphicObjectsAnalysis
Definition: PolymorphicObjects.h:39
souffle::contains
bool contains(const C &container, const typename C::value_type &element)
A utility to check generically whether a given element is contained in a given container.
Definition: ContainerUtil.h:75
souffle::ast::BindingStore::bindVariableStrongly
void bindVariableStrongly(std::string varName)
Mark the given variable as strongly bound.
Definition: BindingStore.h:54
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
souffle::BinaryConstraintOp::EQ
@ EQ
MiscUtil.h
souffle::ast::transform::MagicSetTransformer::getTriviallyIgnoredRelations
static std::set< QualifiedName > getTriviallyIgnoredRelations(const TranslationUnit &tu)
Gets the set of relations that are trivially computable, and so should not be magic-set.
Definition: MagicSet.cpp:70
souffle::ast::Clause
Intermediate representation of a horn clause.
Definition: Clause.h:51
souffle::ast::Program::getClauses
std::vector< Clause * > getClauses() const
Return clauses.
Definition: Program.h:65
souffle::ast::transform::NormaliseDatabaseTransformer
MagicSetTransformer::NormaliseDatabaseTransformer NormaliseDatabaseTransformer
Definition: MagicSet.cpp:60
souffle::ast::Relation::getAttributes
std::vector< Attribute * > getAttributes() const
Get relation attributes.
Definition: Relation.h:83
souffle::ast::analysis::Variable
A variable to be utilized within constraints to be handled by the constraint solver.
Definition: ConstraintSystem.h:41
Relation.h
rhs
Own< Argument > rhs
Definition: ResolveAliases.cpp:185
souffle::BinaryConstraintOp::FEQ
@ FEQ
souffle::FunctorOp::MOD
@ MOD
souffle::ast::Atom
An atom class.
Definition: Atom.h:51
souffle::BinaryConstraintOp::FLT
@ FLT
souffle::ast::Clause::getHead
Atom * getHead() const
Return the atom that represents the head of the clause.
Definition: Clause.h:74
souffle::ast::Counter
counter functor (incrementing a value after each invocation)
Definition: Counter.h:34
Utils.h
souffle::BinaryConstraintOp::FLE
@ FLE
SCCGraph.h
NodeMapper.h
Constant.h
souffle::ast::transform::MagicSetTransformer::LabelDatabaseTransformer::PositiveLabellingTransformer
Runs the second stage of the labelling algorithm.
Definition: MagicSet.h:208
souffle::ast::Argument
An abstract class for arguments.
Definition: Argument.h:33
Global.h
Attribute.h
souffle::ast::transform::AdornDatabaseTransformer
MagicSetTransformer::AdornDatabaseTransformer AdornDatabaseTransformer
Definition: MagicSet.cpp:62
lhs
Own< Argument > lhs
Definition: ResolveAliases.cpp:184
StringConstant.h
souffle::ast::Program
The program class consists of relations, clauses and types.
Definition: Program.h:52
souffle::Relation
Object-oriented wrapper class for Souffle's templatized relations.
Definition: SouffleInterface.h:49
souffle::ast::Atom::getQualifiedName
const QualifiedName & getQualifiedName() const
Return qualified name.
Definition: Atom.h:57
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::mk
Own< A > mk(Args &&... xs)
Definition: ContainerUtil.h:48
souffle::RelationQualifier::MAGIC
@ MAGIC
i
size_t i
Definition: json11.h:663
ContainerUtil.h
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::transform::MagicSetTransformer::getStronglyIgnoredRelations
static std::set< QualifiedName > getStronglyIgnoredRelations(const TranslationUnit &tu)
Gets the set of relations to strongly ignore during the MST process.
Definition: MagicSet.cpp:219
souffle::ast::Negation
Negation of an atom negated atom.
Definition: Negation.h:50
clauses
std::vector< Own< Clause > > clauses
Definition: ComponentInstantiation.cpp:67
StringUtil.h
souffle::ast::TranslationUnit
Translation unit class for the translation pipeline.
Definition: TranslationUnit.h:51
PolymorphicObjects.h
souffle::ast::Program::addClause
void addClause(Own< Clause > clause)
Add a clause.
Definition: Program.cpp:62
souffle::ast::transform::MagicSetTransformer::getWeaklyIgnoredRelations
static std::set< QualifiedName > getWeaklyIgnoredRelations(const TranslationUnit &tu)
Gets the set of relations to weakly ignore during the MST process.
Definition: MagicSet.cpp:96
souffle::test::count
int count(const C &c)
Definition: table_test.cpp:40
TCB_SPAN_NAMESPACE_NAME::get
constexpr auto get(span< E, S > s) -> decltype(s[N])
Definition: span.h:599
souffle::ast::transform
Definition: Program.h:45
souffle::ast::Program::getRelations
std::vector< Relation * > getRelations() const
Return relations.
Definition: Program.h:60
souffle::ast::BindingStore::bindVariableWeakly
void bindVariableWeakly(std::string varName)
Mark the given variable as weakly bound.
Definition: BindingStore.h:66
souffle::ast::DirectiveType::input
@ input
souffle::ast::Program::addRelation
void addRelation(Own< Relation > relation)
Definition: Program.cpp:43
souffle::ast::Aggregator::apply
void apply(const NodeMapper &map) override
Apply the mapper to all child nodes.
Definition: Aggregator.h:100
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::ast::Program::getDirectives
std::vector< Directive * > getDirectives() const
Return relation directives.
Definition: Program.h:75
souffle::isEqConstraint
bool isEqConstraint(const BinaryConstraintOp constraintOp)
Definition: BinaryConstraintOps.h:124
Node.h
souffle::ast::TranslationUnit::getAnalysis
Analysis * getAnalysis() const
get analysis: analysis is generated on the fly if not present
Definition: TranslationUnit.h:60
Aggregator.h
souffle::Global::config
static MainConfig & config()
Definition: Global.h:141
souffle::ast::BindingStore::isBound
bool isBound(std::string varName) const
Check if a variable is bound.
Definition: BindingStore.h:71
souffle::ast::transform::MagicSetCoreTransformer
MagicSetTransformer::MagicSetCoreTransformer MagicSetCoreTransformer
Definition: MagicSet.cpp:63
souffle::detail::brie::copy
auto copy(span< A, arity > s)
Definition: Brie.h:98
souffle::ast::Aggregator
Defines the aggregator class.
Definition: Aggregator.h:53
souffle::ast::transform::NegativeLabellingTransformer
MagicSetTransformer::LabelDatabaseTransformer::NegativeLabellingTransformer NegativeLabellingTransformer
Definition: MagicSet.cpp:66
souffle::FunctorOp::UMOD
@ UMOD
QualifiedName.h
std
Definition: Brie.h:3053
souffle::ast::Program::removeDirective
bool removeDirective(const Directive *directive)
Remove a directive.
Definition: Program.cpp:78
souffle::ast::Negation::getAtom
Atom * getAtom() const
Get negated atom.
Definition: Negation.h:55
Program.h
souffle::ast::QualifiedName::prepend
void prepend(std::string name)
prepend qualifiers
Definition: QualifiedName.h:56
souffle::ast::analysis::SCCGraphAnalysis
Analysis pass computing the strongly connected component (SCC) graph for the datalog program.
Definition: SCCGraph.h:50
RamTypes.h
souffle::ast::Relation::getQualifiedName
const QualifiedName & getQualifiedName() const
Get qualified relation name.
Definition: Relation.h:57
souffle::splitString
std::vector< std::string > splitString(const std::string &str, char delimiter)
Splits a string given a delimiter.
Definition: StringUtil.h:321
BindingStore.h
souffle::ast::analysis::PrecedenceGraphAnalysis
Analysis pass computing the precedence graph of the relations of the datalog progam.
Definition: PrecedenceGraph.h:43
Visitor.h
souffle::ast::transform::LabelDatabaseTransformer
MagicSetTransformer::LabelDatabaseTransformer LabelDatabaseTransformer
Definition: MagicSet.cpp:61
BinaryConstraint.h
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
PrecedenceGraph.h
souffle::BinaryConstraintOp::FGT
@ FGT
souffle::ast::Program::addDirective
void addDirective(Own< Directive > directive)
Add relation directive.
Definition: Program.h:80
souffle::ast::QualifiedName
Qualified Name class defines fully/partially qualified names to identify objects in components.
Definition: QualifiedName.h:39
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::ast::BindingStore
Definition: BindingStore.h:38
souffle::ast::TranslationUnit::getProgram
Program & getProgram() const
Return the program.
Definition: TranslationUnit.h:84
souffle::ast::Program::removeClause
bool removeClause(const Clause *clause)
Remove a clause.
Definition: Program.cpp:68
RecordInit.h
MagicSet.h
NumericConstant.h
souffle::ast::transform::PositiveLabellingTransformer
MagicSetTransformer::LabelDatabaseTransformer::PositiveLabellingTransformer PositiveLabellingTransformer
Definition: MagicSet.cpp:68
souffle::ast::Clause::getBodyLiterals
std::vector< Literal * > getBodyLiterals() const
Obtains a copy of the internally maintained body literals.
Definition: Clause.h:79