souffle  2.0.2-371-g6315b36
BindingStore.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2020, 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 BindingStore.cpp
12  *
13  * Defines the BindingStore class, which can be used to dynamically
14  * determine the set of bound variables within a given clause.
15  *
16  ***********************************************************************/
17 
19 #include "ast/Aggregator.h"
20 #include "ast/Atom.h"
21 #include "ast/BinaryConstraint.h"
22 #include "ast/Clause.h"
23 #include "ast/Constant.h"
24 #include "ast/RecordInit.h"
25 #include "ast/Term.h"
26 #include "ast/Variable.h"
27 #include "ast/utility/Visitor.h"
29 #include <algorithm>
30 #include <cassert>
31 #include <vector>
32 
33 namespace souffle::ast {
34 
35 BindingStore::BindingStore(const Clause* clause) {
38 }
39 
40 void BindingStore::generateBindingDependencies(const Clause* clause) {
41  // Grab all relevant constraints (i.e. eq. constrs not involving aggregators)
42  std::set<const BinaryConstraint*> relevantEqConstraints;
43  visitDepthFirst(*clause, [&](const BinaryConstraint& bc) {
44  bool containsAggregators = false;
45  visitDepthFirst(bc, [&](const Aggregator& /* aggr */) { containsAggregators = true; });
46  if (!containsAggregators && isEqConstraint(bc.getBaseOperator())) {
47  relevantEqConstraints.insert(&bc);
48  }
49  });
50 
51  // Add variable binding dependencies implied by the constraint
52  for (const auto* eqConstraint : relevantEqConstraints) {
53  processEqualityBindings(eqConstraint->getLHS(), eqConstraint->getRHS());
54  processEqualityBindings(eqConstraint->getRHS(), eqConstraint->getLHS());
55  }
56 }
57 
58 void BindingStore::processEqualityBindings(const Argument* lhs, const Argument* rhs) {
59  // Only care about equalities affecting the bound status of variables
60  const auto* var = dynamic_cast<const Variable*>(lhs);
61  if (var == nullptr) return;
62 
63  // If all variables on the rhs are bound, then lhs is also bound
65  visitDepthFirst(*rhs, [&](const Variable& subVar) { depSet.insert(subVar.getName()); });
66  addBindingDependency(var->getName(), depSet);
67 
68  // If the lhs is bound, then all args in the rec on the rhs are also bound
69  if (const auto* rec = dynamic_cast<const RecordInit*>(rhs)) {
70  for (const auto* arg : rec->getArguments()) {
71  const auto* subVar = dynamic_cast<const Variable*>(arg);
72  assert(subVar != nullptr && "expected args to be variables");
73  addBindingDependency(subVar->getName(), BindingStore::ConjBindingSet({var->getName()}));
74  }
75  }
76 }
77 
79  const BindingStore::ConjBindingSet& origDependency) {
80  BindingStore::ConjBindingSet newDependency;
81  for (const auto& var : origDependency) {
82  // Only keep unbound variables in the dependency
83  if (!contains(stronglyBoundVariables, var)) {
84  newDependency.insert(var);
85  }
86  }
87  return newDependency;
88 }
89 
91  const BindingStore::DisjBindingSet& origDependency) {
92  BindingStore::DisjBindingSet newDependencies;
93  for (const auto& dep : origDependency) {
94  auto newDep = reduceDependency(dep);
95  if (!newDep.empty()) {
96  newDependencies.insert(newDep);
97  }
98  }
99  return newDependencies;
100 }
101 
103  bool changed = false;
104  std::map<std::string, BindingStore::DisjBindingSet> newVariableDependencies;
105  std::set<std::string> variablesToBind;
106 
107  // Reduce each variable's set of dependencies one by one
108  for (const auto& [headVar, dependencies] : variableDependencies) {
109  // No need to track the dependencies of already-bound variables
110  if (contains(stronglyBoundVariables, headVar)) {
111  changed = true;
112  continue;
113  }
114 
115  // Reduce the dependency set based on bound variables
116  auto newDependencies = reduceDependency(dependencies);
117  if (newDependencies.empty() || newDependencies.size() < dependencies.size()) {
118  // At least one dependency has been satisfied, so variable is now bound
119  changed = true;
120  variablesToBind.insert(headVar);
121  continue;
122  }
123  newVariableDependencies[headVar] = newDependencies;
124  changed |= (newDependencies != dependencies);
125  }
126 
127  // Bind variables that need to be bound
128  for (auto var : variablesToBind) {
129  stronglyBoundVariables.insert(var);
130  }
131 
132  // Repeat it recursively if any changes happened, until we reach a fixpoint
133  if (changed) {
134  variableDependencies = newVariableDependencies;
136  return true;
137  }
138  assert(variableDependencies == newVariableDependencies && "unexpected change");
139  return false;
140 }
141 
142 bool BindingStore::isBound(const Argument* arg) const {
143  if (const auto* var = dynamic_cast<const Variable*>(arg)) {
144  return isBound(var->getName());
145  } else if (const auto* term = dynamic_cast<const Term*>(arg)) {
146  for (const auto* subArg : term->getArguments()) {
147  if (!isBound(subArg)) {
148  return false;
149  }
150  }
151  return true;
152  } else if (isA<Constant>(arg)) {
153  return true;
154  } else {
155  return false;
156  }
157 }
158 
159 size_t BindingStore::numBoundArguments(const Atom* atom) const {
160  size_t count = 0;
161  for (const auto* arg : atom->getArguments()) {
162  if (isBound(arg)) {
163  count++;
164  }
165  }
166  return count;
167 }
168 
169 } // namespace souffle::ast
BinaryConstraintOps.h
souffle::ast::BindingStore::BindingStore
BindingStore(const Clause *clause)
Definition: BindingStore.cpp:42
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::reduceDependency
ConjBindingSet reduceDependency(const ConjBindingSet &origDependency)
Reduce a conjunctive set of dependencies based on the current bound variable set.
Definition: BindingStore.cpp:85
souffle::ast::BindingStore::DisjBindingSet
std::set< ConjBindingSet > DisjBindingSet
Definition: BindingStore.h:84
souffle::ast::analysis::Variable
A variable to be utilized within constraints to be handled by the constraint solver.
Definition: ConstraintSystem.h:41
rhs
Own< Argument > rhs
Definition: ResolveAliases.cpp:185
souffle::ast::BindingStore::generateBindingDependencies
void generateBindingDependencies(const Clause *clause)
Generate all binding dependencies implied by the constraints within a given clause.
Definition: BindingStore.cpp:47
Constant.h
lhs
Own< Argument > lhs
Definition: ResolveAliases.cpp:184
souffle::ast::BindingStore::stronglyBoundVariables
std::set< std::string > stronglyBoundVariables
Definition: BindingStore.h:86
souffle::ast::BindingStore::ConjBindingSet
std::set< std::string > ConjBindingSet
Definition: BindingStore.h:83
Term.h
souffle::ast::BindingStore::reduceDependencies
bool reduceDependencies()
Reduce the full set of dependencies for all tracked variables, binding whatever needs to be bound.
Definition: BindingStore.cpp:109
Atom.h
souffle::ast::BinaryConstraint::getBaseOperator
BinaryConstraintOp getBaseOperator() const
Return binary operator.
Definition: BinaryConstraint.h:69
souffle::test::count
int count(const C &c)
Definition: table_test.cpp:40
souffle::isEqConstraint
bool isEqConstraint(const BinaryConstraintOp constraintOp)
Definition: BinaryConstraintOps.h:124
souffle::ast::BinaryConstraint
Binary constraint class.
Definition: BinaryConstraint.h:53
Aggregator.h
souffle::ast::BindingStore::isBound
bool isBound(std::string varName) const
Check if a variable is bound.
Definition: BindingStore.h:71
souffle::ast::BindingStore::processEqualityBindings
void processEqualityBindings(const Argument *lhs, const Argument *rhs)
Add binding dependencies formed on lhs by a <lhs> = <rhs> equality constraint.
Definition: BindingStore.cpp:65
souffle::ast::Aggregator
Defines the aggregator class.
Definition: Aggregator.h:53
souffle::ast::BindingStore::addBindingDependency
void addBindingDependency(std::string variable, ConjBindingSet dependency)
Add a new conjunction of variables as a potential binder for a given variable.
Definition: BindingStore.h:94
BindingStore.h
souffle::ast::BindingStore::numBoundArguments
size_t numBoundArguments(const Atom *atom) const
Counts the number of bound arguments in the given atom.
Definition: BindingStore.cpp:166
Visitor.h
Clause.h
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
souffle::ast
Definition: Aggregator.h:35
souffle::ast::BindingStore::variableDependencies
std::map< std::string, DisjBindingSet > variableDependencies
Definition: BindingStore.h:88
RecordInit.h
souffle::ast::RecordInit
Defines a record initialization class.
Definition: RecordInit.h:42
Variable.h