souffle  2.0.2-371-g6315b36
Public Member Functions | Private Member Functions
souffle::ast::transform::ReduceExistentialsTransformer Class Reference

Transformation pass to reduce unnecessary computation for relations that only appear in the form A(_,...,_). More...

#include <ReduceExistentials.h>

Inheritance diagram for souffle::ast::transform::ReduceExistentialsTransformer:
Inheritance graph
Collaboration diagram for souffle::ast::transform::ReduceExistentialsTransformer:
Collaboration graph

Public Member Functions

ReduceExistentialsTransformerclone () 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
 

Detailed Description

Transformation pass to reduce unnecessary computation for relations that only appear in the form A(_,...,_).

Definition at line 37 of file ReduceExistentials.h.

Member Function Documentation

◆ clone()

ReduceExistentialsTransformer* souffle::ast::transform::ReduceExistentialsTransformer::clone ( ) const
inlineoverridevirtual

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

Definition at line 50 of file ReduceExistentials.h.

◆ getName()

std::string souffle::ast::transform::ReduceExistentialsTransformer::getName ( ) const
inlineoverridevirtual

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

Definition at line 46 of file ReduceExistentials.h.

◆ transform()

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

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

Definition at line 48 of file ReduceExistentials.cpp.

48  {
49  for (Argument* arg : atom.getArguments()) {
50  if (!isA<UnnamedVariable>(arg)) {
51  return false;
52  }
53  }
54  return true;
55  };
56 
57  // Construct a dependency graph G where:
58  // - Each relation is a node
59  // - An edge (a,b) exists iff a uses b "non-existentially" in one of its *recursive* clauses
60  // This way, a relation can be transformed into an existential form
61  // if and only if all its predecessors can also be transformed.
62  Graph<QualifiedName> relationGraph = Graph<QualifiedName>();
63 
64  // Add in the nodes
65  for (Relation* relation : program.getRelations()) {
66  relationGraph.insert(relation->getQualifiedName());
67  }
68 
69  // Keep track of all relations that cannot be transformed
70  std::set<QualifiedName> minimalIrreducibleRelations;
71 
72  auto* ioType = translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
73 
74  for (Relation* relation : program.getRelations()) {
75  // No I/O relations can be transformed
76  if (ioType->isIO(relation)) {
77  minimalIrreducibleRelations.insert(relation->getQualifiedName());
78  }
79  for (Clause* clause : getClauses(program, *relation)) {
80  bool recursive = isRecursiveClause(*clause);
81  visitDepthFirst(*clause, [&](const Atom& atom) {
82  if (atom.getQualifiedName() == clause->getHead()->getQualifiedName()) {
83  return;
84  }
85 
86  if (!isExistentialAtom(atom)) {
87  if (recursive) {
88  // Clause is recursive, so add an edge to the dependency graph
89  relationGraph.insert(clause->getHead()->getQualifiedName(), atom.getQualifiedName());
90  } else {
91  // Non-existential apperance in a non-recursive clause, so
92  // it's out of the picture
93  minimalIrreducibleRelations.insert(atom.getQualifiedName());
94  }
95  }
96  });
97  }
98  }
99 
100  // TODO (see issue #564): Don't transform relations appearing in aggregators
101  // due to aggregator issues with unnamed variables.
102  visitDepthFirst(program, [&](const Aggregator& aggr) {
104  aggr, [&](const Atom& atom) { minimalIrreducibleRelations.insert(atom.getQualifiedName()); });
105  });
106 
107  // Run a DFS from each 'bad' source
108  // A node is reachable in a DFS from an irreducible node if and only if it is
109  // also an irreducible node
110  std::set<QualifiedName> irreducibleRelations;
111  for (QualifiedName relationName : minimalIrreducibleRelations) {
112  relationGraph.visitDepthFirst(
113  relationName, [&](const QualifiedName& subRel) { irreducibleRelations.insert(subRel); });
114  }
115 
116  // All other relations are necessarily existential
117  std::set<QualifiedName> existentialRelations;
118  for (Relation* relation : program.getRelations()) {
119  if (!getClauses(program, *relation).empty() && relation->getArity() != 0 &&
120  irreducibleRelations.find(relation->getQualifiedName()) == irreducibleRelations.end()) {
121  existentialRelations.insert(relation->getQualifiedName());
122  }
123  }
124 
125  // Reduce the existential relations
126  for (QualifiedName relationName : existentialRelations) {
127  Relation* originalRelation = getRelation(program, relationName);
128 
129  std::stringstream newRelationName;
130  newRelationName << "+?exists_" << relationName;
131 
132  auto newRelation = mk<Relation>();
133  newRelation->setQualifiedName(newRelationName.str());
134  newRelation->setSrcLoc(originalRelation->getSrcLoc());
135 
136  // EqRel relations require two arguments, so remove it from the qualifier
137  if (newRelation->getRepresentation() == RelationRepresentation::EQREL) {
138  newRelation->setRepresentation(RelationRepresentation::DEFAULT);
139  }
140 
141  // Keep all non-recursive clauses
142  for (Clause* clause : getClauses(program, *originalRelation)) {
143  if (!isRecursiveClause(*clause)) {
144  auto newClause = mk<Clause>();
145 
146  newClause->setSrcLoc(clause->getSrcLoc());
147  if (const ExecutionPlan* plan = clause->getExecutionPlan()) {
148  newClause->setExecutionPlan(souffle::clone(plan));
149  }
150  newClause->setHead(mk<Atom>(newRelationName.str()));
151  for (Literal* lit : clause->getBodyLiterals()) {
152  newClause->addToBody(souffle::clone(lit));
153  }
154 
155  program.addClause(std::move(newClause));
156  }
157  }
158 
159  program.addRelation(std::move(newRelation));
160  }
161 
162  // Mapper that renames the occurrences of marked relations with their existential
163  // counterparts
164  struct renameExistentials : public NodeMapper {
165  const std::set<QualifiedName>& relations;
166 
167  renameExistentials(std::set<QualifiedName>& relations) : relations(relations) {}
168 
169  Own<Node> operator()(Own<Node> node) const override {
170  if (auto* clause = dynamic_cast<Clause*>(node.get())) {
171  if (relations.find(clause->getHead()->getQualifiedName()) != relations.end()) {
172  // Clause is going to be removed, so don't rename it
173  return node;
174  }
175  } else if (auto* atom = dynamic_cast<Atom*>(node.get())) {
176  if (relations.find(atom->getQualifiedName()) != relations.end()) {
177  // Relation is now existential, so rename it
178  std::stringstream newName;
179  newName << "+?exists_" << atom->getQualifiedName();
180  return mk<Atom>(newName.str());
181  }
182  }
183  node->apply(*this);
184  return node;
185  }
186  };
187 
188  renameExistentials update(existentialRelations);
189  program.apply(update);
190 
191  bool changed = !existentialRelations.empty();
192  return changed;
193 }
194 
195 } // namespace souffle::ast::transform

The documentation for this class was generated from the following files:
souffle::RelationRepresentation::EQREL
@ EQREL
relation
Relation & relation
Definition: Reader.h:130
relations
std::vector< Own< Relation > > relations
Definition: ComponentInstantiation.cpp:65
souffle::ast::isRecursiveClause
bool isRecursiveClause(const Clause &clause)
Returns whether the given clause is recursive.
Definition: Utils.cpp:203
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
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::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::RelationRepresentation::DEFAULT
@ DEFAULT
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