souffle  2.0.2-371-g6315b36
SimplifyAggregateTargetExpression.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 SimplifyAggregateTargetExpression.cpp
12  *
13  ***********************************************************************/
14 
16 #include "ast/Argument.h"
17 #include "ast/TranslationUnit.h"
18 #include "ast/analysis/Aggregate.h"
20 #include "ast/utility/NodeMapper.h"
21 #include "ast/utility/Visitor.h"
22 
23 namespace souffle::ast::transform {
24 
25 bool SimplifyAggregateTargetExpressionTransformer::transform(TranslationUnit& translationUnit) {
26  Program& program = translationUnit.getProgram();
27  // Map all aggregates with complex target expressions to aggregates
28  // with simple target expressions and an extra equality literal in the body
29  // Note: we need to be careful that we don't inadvertently turn
30  // a local variable (recognised as local by the fact that it occurs in the target expression)
31  // to an injected variable.
32  // I.e it is possible that this happens:
33  // .. :- A(y), x = sum y + z : { B(y, z) }
34  // -> :- A(y), x = sum z0: { B(y, z), z0 = y + z.
35  struct AggregateTESimplifier : public NodeMapper {
36  mutable bool changed = false;
37  TranslationUnit* tu;
38  const Clause* originatingClause;
39 
40  AggregateTESimplifier(TranslationUnit* unit, const Clause* c) : tu(unit), originatingClause(c) {}
41 
42  bool causedChange() {
43  return changed;
44  }
45  std::unique_ptr<Node> operator()(std::unique_ptr<Node> node) const override {
46  if (auto* aggregate = dynamic_cast<Aggregator*>(node.get())) {
47  // Check if the target expression is complex
48  if (aggregate->getTargetExpression() != nullptr &&
49  dynamic_cast<const Variable*>(aggregate->getTargetExpression()) == nullptr) {
50  // If it's complex, come up with a unique variable name to stand
51  // in the place of the target expression. This will be set equal to the target
52  // expression.
53  // What we might have though now is that a variable in the TE was shadowing
54  // a variable from the outer scope. Now we have "forgotten" this shadowing
55  // and need to restore it by scoping that variable properly.
56  // We know that a variable from the TE is shadowing another variable
57  // if a variable with the same name appears range-restricted in the outer scope.
58 
59  // make a unique target expression variable
60  auto newTargetExpression =
61  mk<Variable>(analysis::findUniqueVariableName(*originatingClause, "x"));
62  auto equalityLiteral = std::make_unique<BinaryConstraint>(BinaryConstraintOp::EQ,
63  souffle::clone(newTargetExpression),
64  souffle::clone(aggregate->getTargetExpression()));
65  std::vector<std::unique_ptr<Literal>> newBody;
66  for (Literal* literal : aggregate->getBodyLiterals()) {
67  newBody.push_back(souffle::clone(literal));
68  }
69  newBody.push_back(std::move(equalityLiteral));
70  // If there are occurrences of the same variable in the outer scope
71  // Then we need to be careful. There are two ensuing situations:
72  // 1) The variable in the outer scope is ungrounded (or occurs in the head)
73  // => We have a witness, and we shouldn't rename this variable, because it is not
74  // local.
75  // 2) The variable in the outer scope is grounded
76  // => We need to rename this because it is a local variable (the grounding of the
77  // outer scope
78  // variable is shadowed by occurrence of the variable in the target expression)
79 
80  // We already have a way to find witnesses and also to find variables occurring outside
81  // this aggregate. We will take the set minus of variablesOccurringOutside - witnesses.
82  // Whichever variables are in this set need to be renamed within the aggregate subclause.
83  auto witnesses = analysis::getWitnessVariables(*tu, *originatingClause, *aggregate);
84  std::set<std::string> varsOutside =
85  analysis::getVariablesOutsideAggregate(*originatingClause, *aggregate);
86 
87  std::set<std::string> varsGroundedOutside;
88  for (auto& varName : varsOutside) {
89  if (witnesses.find(varName) == witnesses.end()) {
90  varsGroundedOutside.insert(varName);
91  }
92  }
93  // rename all variables that were grounded outside
94  // (we had an occurrence of the same variable name in the TE,
95  // the implication being that the occurrence of that variable
96  // in the scope of the aggregate subclause should be local,
97  // not grounded from the outer scope (injected)
98  visitDepthFirst(*aggregate->getTargetExpression(), [&](const Variable& v) {
99  if (varsGroundedOutside.find(v.getName()) != varsGroundedOutside.end()) {
100  // rename it everywhere in the body so that we've scoped this properly.
101  std::string newVarName =
102  analysis::findUniqueVariableName(*originatingClause, v.getName());
103  for (auto& literal : newBody) {
104  visitDepthFirst(*literal, [&](const Variable& literalVar) {
105  if (literalVar == v) {
106  const_cast<Variable&>(literalVar).setName(newVarName);
107  }
108  });
109  }
110  }
111  });
112 
113  // set up a new aggregate to replace this one
114  auto newAggregate = mk<Aggregator>(
115  aggregate->getBaseOperator(), std::move(newTargetExpression), std::move(newBody));
116  changed = true;
117  return newAggregate;
118  }
119  }
120  node->apply(*this);
121  return node;
122  }
123  };
124 
125  bool changed = false;
126  visitDepthFirst(program, [&](const Clause& clause) {
127  Own<Clause> oldClause = souffle::clone(&clause);
128  AggregateTESimplifier teSimplifier(&translationUnit, oldClause.get());
129  const_cast<Clause&>(clause).apply(teSimplifier);
130  changed = changed || teSimplifier.causedChange();
131  });
132  return changed;
133 }
134 } // namespace souffle::ast::transform
TranslationUnit.h
GroundWitnesses.h
souffle::ast::NodeMapper
An abstract class for manipulating AST Nodes by substitution.
Definition: NodeMapper.h:36
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
souffle::BinaryConstraintOp::EQ
@ EQ
souffle::ast::Clause
Intermediate representation of a horn clause.
Definition: Clause.h:51
souffle::ast::analysis::Variable
A variable to be utilized within constraints to be handled by the constraint solver.
Definition: ConstraintSystem.h:41
Aggregate.h
NodeMapper.h
Argument.h
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::ast::analysis::getVariablesOutsideAggregate
std::set< std::string > getVariablesOutsideAggregate(const Clause &clause, const Aggregator &aggregate)
Computes the set of variables occurring outside the aggregate.
Definition: Aggregate.cpp:164
souffle::ast::TranslationUnit
Translation unit class for the translation pipeline.
Definition: TranslationUnit.h:51
souffle::ast::Literal
Defines an abstract class for literals in a horn clause.
Definition: Literal.h:36
souffle::ast::transform
Definition: Program.h:45
souffle::ast::Aggregator
Defines the aggregator class.
Definition: Aggregator.h:53
souffle::ast::analysis::findUniqueVariableName
std::string findUniqueVariableName(const Clause &clause, std::string base)
Find a variable name using base to form a string like base1 Use this when you need to limit the scope...
Definition: Aggregate.cpp:179
Visitor.h
souffle::ast::transform::SimplifyAggregateTargetExpressionTransformer::transform
bool transform(TranslationUnit &translationUnit) override
Definition: SimplifyAggregateTargetExpression.cpp:29
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
SimplifyAggregateTargetExpression.h
souffle::ast::analysis::getWitnessVariables
std::set< std::string > getWitnessVariables(const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
Computes the set of witness variables that are used in the aggregate A variable is a witness if it oc...
Definition: Aggregate.cpp:75