souffle  2.0.2-371-g6315b36
MaterializeSingletonAggregation.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 MaterializeSingletonAggregation.cpp
12  *
13  ***********************************************************************/
14 
16 #include "ast/Aggregator.h"
17 #include "ast/Argument.h"
18 #include "ast/Atom.h"
19 #include "ast/Attribute.h"
20 #include "ast/BinaryConstraint.h"
21 #include "ast/Clause.h"
22 #include "ast/Literal.h"
23 #include "ast/Node.h"
24 #include "ast/Program.h"
25 #include "ast/QualifiedName.h"
26 #include "ast/Relation.h"
27 #include "ast/TranslationUnit.h"
28 #include "ast/Variable.h"
29 #include "ast/analysis/Aggregate.h"
30 #include "ast/utility/NodeMapper.h"
31 #include "ast/utility/Utils.h"
32 #include "ast/utility/Visitor.h"
36 #include <cassert>
37 #include <map>
38 #include <memory>
39 #include <set>
40 #include <utility>
41 #include <vector>
42 
43 namespace souffle::ast::transform {
44 
45 bool MaterializeSingletonAggregationTransformer::transform(TranslationUnit& translationUnit) {
46  Program& program = translationUnit.getProgram();
47  std::set<std::pair<Aggregator*, Clause*>> pairs;
48  // avoid trying to deal with inner aggregates directly.
49  // We will apply a fixpoint operator so that it ends up all getting
50  // wound out but we can't rush this.
51  std::set<const Aggregator*> innerAggregates;
52  visitDepthFirst(program, [&](const Aggregator& agg) {
53  visitDepthFirst(agg, [&](const Aggregator& innerAgg) {
54  if (agg != innerAgg) {
55  innerAggregates.insert(&innerAgg);
56  }
57  });
58  });
59 
60  // collect references to clause / aggregate pairs
61  visitDepthFirst(program, [&](const Clause& clause) {
62  visitDepthFirst(clause, [&](const Aggregator& agg) {
63  // only unroll one level at a time
64  if (innerAggregates.find(&agg) != innerAggregates.end()) {
65  return;
66  }
67  // if the aggregate isn't single valued
68  // (ie it relies on a grounding from the outer scope)
69  // or it's a constituent of the only atom in the clause,
70  // then there's no point materialising it!
71  if (!isSingleValued(translationUnit, agg, clause) || clause.getBodyLiterals().size() == 1) {
72  return;
73  }
74  auto* foundAggregate = const_cast<Aggregator*>(&agg);
75  auto* foundClause = const_cast<Clause*>(&clause);
76  pairs.insert(std::make_pair(foundAggregate, foundClause));
77  });
78  });
79  for (auto pair : pairs) {
80  // Clone the aggregate that we're going to be deleting.
81  auto aggregate = souffle::clone(pair.first);
82  Clause* clause = pair.second;
83  // synthesise an aggregate relation
84  // __agg_rel_0()
85  auto aggRel = mk<Relation>();
86  auto aggHead = mk<Atom>();
87  auto aggClause = mk<Clause>();
88 
89  std::string aggRelName = analysis::findUniqueRelationName(program, "__agg");
90  aggRel->setQualifiedName(aggRelName);
91  aggHead->setQualifiedName(aggRelName);
92 
93  // create a synthesised variable to replace the aggregate term!
94  std::string variableName = analysis::findUniqueVariableName(*clause, "z");
95  auto variable = mk<ast::Variable>(variableName);
96 
97  // __agg_rel_0(z) :- ...
98  aggHead->addArgument(souffle::clone(variable));
99  aggRel->addAttribute(mk<Attribute>(variableName, "number"));
100  aggClause->setHead(souffle::clone(aggHead));
101 
102  // A(x) :- x = sum .., B(x).
103  // -> A(x) :- x = z, B(x), __agg_rel_0(z).
104  auto equalityLiteral = mk<BinaryConstraint>(
105  BinaryConstraintOp::EQ, souffle::clone(variable), souffle::clone(aggregate));
106  // __agg_rel_0(z) :- z = sum ...
107  aggClause->addToBody(std::move(equalityLiteral));
108  program.addRelation(std::move(aggRel));
109  program.addClause(std::move(aggClause));
110 
111  // the only thing left to do is just replace the aggregate terms in the original
112  // clause with the synthesised variable
113  struct replaceAggregate : public NodeMapper {
114  const Aggregator& aggregate;
115  const Own<ast::Variable> variable;
116  replaceAggregate(const Aggregator& aggregate, Own<ast::Variable> variable)
117  : aggregate(aggregate), variable(std::move(variable)) {}
118  Own<Node> operator()(Own<Node> node) const override {
119  if (auto* current = dynamic_cast<Aggregator*>(node.get())) {
120  if (*current == aggregate) {
121  auto replacement = souffle::clone(variable);
122  assert(replacement != nullptr);
123  return replacement;
124  }
125  }
126  node->apply(*this);
127  return node;
128  }
129  };
130  replaceAggregate update(*aggregate, std::move(variable));
131  clause->apply(update);
132  clause->addToBody(std::move(aggHead));
133  }
134  return pairs.size() > 0;
135 }
136 
138  const TranslationUnit& tu, const Aggregator& agg, const Clause& clause) {
139  // An aggregate is single valued as long as it is not complex.
140  // This just means there are NO injected variables in the aggregate.
141  auto injectedVariables = analysis::getInjectedVariables(tu, clause, agg);
142  return injectedVariables.empty();
143 }
144 
145 } // namespace souffle::ast::transform
souffle::ast::transform::MaterializeSingletonAggregationTransformer::transform
bool transform(TranslationUnit &translationUnit) override
Definition: MaterializeSingletonAggregation.cpp:49
BinaryConstraintOps.h
TranslationUnit.h
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
souffle::BinaryConstraintOp::EQ
@ EQ
MiscUtil.h
souffle::ast::Clause
Intermediate representation of a horn clause.
Definition: Clause.h:51
Relation.h
Aggregate.h
souffle::ast::transform::MaterializeSingletonAggregationTransformer::isSingleValued
static bool isSingleValued(const TranslationUnit &tu, const Aggregator &agg, const Clause &clause)
Determines whether an aggregate is single-valued, ie the aggregate does not depend on the outer scope...
Definition: MaterializeSingletonAggregation.cpp:141
Utils.h
NodeMapper.h
Attribute.h
Argument.h
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
StringUtil.h
Atom.h
Literal.h
souffle::ast::transform
Definition: Program.h:45
souffle::ast::analysis::findUniqueRelationName
std::string findUniqueRelationName(const Program &program, std::string base)
Find a new relation name.
Definition: Aggregate.cpp:190
Node.h
Aggregator.h
souffle::ast::Aggregator
Defines the aggregator class.
Definition: Aggregator.h:53
QualifiedName.h
std
Definition: Brie.h:3053
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
Program.h
Visitor.h
Clause.h
MaterializeSingletonAggregation.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::Clause::getBodyLiterals
std::vector< Literal * > getBodyLiterals() const
Obtains a copy of the internally maintained body literals.
Definition: Clause.h:79
Variable.h
souffle::ast::analysis::getInjectedVariables
std::set< std::string > getInjectedVariables(const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate)
Given an aggregate and a clause, we find all the variables that have been injected into the aggregate...
Definition: Aggregate.cpp:205