souffle  2.0.2-371-g6315b36
ReduceExistentials.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2015, Oracle and/or its affiliates. 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 ReduceExistentials.cpp
12  *
13  ***********************************************************************/
14 
16 #include "GraphUtils.h"
17 #include "RelationTag.h"
18 #include "ast/Aggregator.h"
19 #include "ast/Argument.h"
20 #include "ast/Atom.h"
21 #include "ast/Clause.h"
22 #include "ast/ExecutionPlan.h"
23 #include "ast/Literal.h"
24 #include "ast/Node.h"
25 #include "ast/Program.h"
26 #include "ast/QualifiedName.h"
27 #include "ast/Relation.h"
28 #include "ast/TranslationUnit.h"
29 #include "ast/UnnamedVariable.h"
30 #include "ast/analysis/IOType.h"
31 #include "ast/utility/NodeMapper.h"
32 #include "ast/utility/Utils.h"
33 #include "ast/utility/Visitor.h"
35 #include <functional>
36 #include <memory>
37 #include <ostream>
38 #include <set>
39 #include <utility>
40 #include <vector>
41 
42 namespace souffle::ast::transform {
43 
44 bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) {
45  Program& program = translationUnit.getProgram();
46 
47  // Checks whether an atom is of the form a(_,_,...,_)
48  auto isExistentialAtom = [&](const Atom& atom) {
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.
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
TranslationUnit.h
souffle::Graph::insert
void insert(const Vertex &from, const Vertex &to)
Adds a new edge from the given vertex to the target vertex.
Definition: GraphUtils.h:51
souffle::Graph::visitDepthFirst
void visitDepthFirst(const Vertex &vertex, const Lambda &lambda) const
A generic utility for depth-first visits.
Definition: GraphUtils.h:130
UnnamedVariable.h
souffle::ast::transform::ReduceExistentialsTransformer::transform
bool transform(TranslationUnit &translationUnit) override
Definition: ReduceExistentials.cpp:48
ExecutionPlan.h
IOType.h
souffle::ast::analysis::IOTypeAnalysis
Definition: IOType.h:39
souffle::RelationRepresentation::EQREL
@ EQREL
relation
Relation & relation
Definition: Reader.h:130
MiscUtil.h
souffle::ast::Relation
Defines a relation with a name, attributes, qualifiers, and internal representation.
Definition: Relation.h:49
relations
std::vector< Own< Relation > > relations
Definition: ComponentInstantiation.cpp:65
Relation.h
souffle::ast::Atom
An atom class.
Definition: Atom.h:51
Utils.h
NodeMapper.h
souffle::ast::isRecursiveClause
bool isRecursiveClause(const Clause &clause)
Returns whether the given clause is recursive.
Definition: Utils.cpp:203
souffle::ast::Argument
An abstract class for arguments.
Definition: Argument.h:33
Argument.h
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::Graph
A simple graph structure for graph-based operations.
Definition: GraphUtils.h:40
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
RelationTag.h
Atom.h
Literal.h
GraphUtils.h
ReduceExistentials.h
souffle::ast::transform
Definition: Program.h:45
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
Node.h
Aggregator.h
QualifiedName.h
Program.h
souffle::RelationRepresentation::DEFAULT
@ DEFAULT
Visitor.h
Clause.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