souffle
2.0.2-371-g6315b36
|
Transformation pass to create artificial relations for bodies of aggregation functions consisting of more than a single atom. More...
#include <MaterializeAggregationQueries.h>
Public Member Functions | |
MaterializeAggregationQueriesTransformer * | clone () const override |
std::string | getName () const override |
Public Member Functions inherited from souffle::ast::transform::Transformer | |
bool | apply (TranslationUnit &translationUnit) |
virtual | ~Transformer ()=default |
Static Public Member Functions | |
static bool | materializeAggregationQueries (TranslationUnit &translationUnit) |
Creates artificial relations for bodies of aggregation functions consisting of more than a single atom, in the given program. More... | |
Private Member Functions | |
bool | transform (TranslationUnit &translationUnit) override |
Static Private Member Functions | |
static std::set< std::string > | distinguishHeadArguments (const TranslationUnit &tu, const Clause &clause, const Aggregator &aggregate) |
When we materialise an aggregate subclause, it's a good question which variables belong in the head of that materialised clause and which don't. More... | |
static void | groundInjectedParameters (const TranslationUnit &translationUnit, Clause &aggClause, const Clause &originalClause, const Aggregator &aggregate) |
Modify the aggClause by adding in grounding literals for every variable that appears in the clause ungrounded. More... | |
static void | instantiateUnnamedVariables (Clause &aggClause) |
Whatever variables have been left unnamed have significance for a count aggregate. More... | |
static bool | needsMaterializedRelation (const Aggregator &agg) |
A test determining whether the body of a given aggregation needs to be 'outlined' into an independent relation or can be kept inline. More... | |
Transformation pass to create artificial relations for bodies of aggregation functions consisting of more than a single atom.
Definition at line 37 of file MaterializeAggregationQueries.h.
|
inlineoverridevirtual |
Implements souffle::ast::transform::Transformer.
Definition at line 59 of file MaterializeAggregationQueries.h.
|
staticprivate |
When we materialise an aggregate subclause, it's a good question which variables belong in the head of that materialised clause and which don't.
For a count aggregate for example, it's important that all variables (even unnamed ones) appearing in the subclause are given a space in the head. For min and max aggregates, we know that the entire subclause (as is the case for any aggregate) will need to appear as the body of the materialised rule, but what goes in the head is a bit less straightforward. We could get away with only exporting the column that we are taking the maximum over, because this is the only value that we need to retrieve. The same can be said for sum and mean.
BUT there is a caveat: We support the construct of witnesses to an aggregate calculation. A witness is some variable present in the aggregate subclause that is exported ungrounded to the base scope. If we do save all configurations of the variables in the aggregate subclause (as we are doing when we materialise it), then we can reuse this materialised subclause to retrieve the witness almost instantly.
(This is assuming that we have a good enough machinery to recognise when rules are exactly the same, because the witness transformation actually has to take place BEFORE the materialisation, meaning that there will be two separate relations that actually will represent exactly the same relation semantically, and we would want those to be recognised as a single relation)
So not only then (assuming we have good "equality of rules" machinery) does using the "count" aggregate technique (just giving each variable a place in the head) allow us to save and retrieve witnesses easily, but it also just makes the treatment of aggregates uniform, which I suppose is simpler, nicer, more aesthetic maybe.
Giving min/max/mean/sum aggregate materialised relations only a single column would be far more efficient, but then we would lose the ability to retrieve witnesses. So this was the tradeoff, and losing witnesses is just not an option. We NEED them to be able to give the user witness functionality.
To clarify, we don't want to include local variables of any inner aggregates appearing in this aggregate. My reasoning for deciding this was really just a hunch that I felt like local variables of an inner aggregate should be totally irrelevant. So we include "immediate" local variables, not local variables of any inner aggregate.
The other thing we must include is any injected variables. Depending on the assignment of the injected variable, the aggregate's value may change, but so, still, why would it be important to include the injected variable in the head then?
A(x, n) :- B(x), n = sum y : { C(y, x) }.
Because we need the assignment of x in the outer scope to COINCIDE with the assignment of x in the inner scope. The only thing visible to the outer scope is whatever is in the head. That is good enough justification. And this applies to any type of aggregate.
So the overall conclusion is that what we should include are:
The head atom should contain immediate local and injected variables. No witnesses! They have already been transformed away. This means that we exclude any inner aggregate local variables. But we do NOT exclude inner aggregate injected variables!! It's important that the injected variable ends up in this head so that we do not obfuscate the injected variable's relationship to the outer scope. It does not affect the aggregate value because adding an extra column for the injected variable, where that column will only have one value at a time, will essentially replicate the aggregate body relation for as many possible values of the injected variable that there are. The fact that the injected variable will take one value at a time is key.
Definition at line 77 of file MaterializeAggregationQueries.cpp.
|
inlineoverridevirtual |
Implements souffle::ast::transform::Transformer.
Definition at line 46 of file MaterializeAggregationQueries.h.
|
staticprivate |
Modify the aggClause by adding in grounding literals for every variable that appears in the clause ungrounded.
The source of literals to copy from is the originalClause.
Mask inner aggregates to make sure we don't consider them grounded and everything.
Go through body literals. If the literal is an atom, then replace the atom with a negated version of the atom, so that injected parameters that occur in an inner aggregate don't "seem" grounded.
Definition at line 114 of file MaterializeAggregationQueries.cpp.
References souffle::clone().
|
staticprivate |
Whatever variables have been left unnamed have significance for a count aggregate.
They NEED to be in the head of the materialized atom that will replace this aggregate subclause. Therefore, we give them dummy names (_n rather than just _) so that the count will be correct.
Definition at line 53 of file MaterializeAggregationQueries.cpp.
References souffle::test::count(), and souffle::toString().
|
static |
Creates artificial relations for bodies of aggregation functions consisting of more than a single atom, in the given program.
program | the program to be processed |
GENERAL PROCEDURE FOR MATERIALISING THE BODY OF AN AGGREGATE: NB:
Definition at line 249 of file MaterializeAggregationQueries.cpp.
References souffle::ast::visitDepthFirst().
|
staticprivate |
A test determining whether the body of a given aggregation needs to be 'outlined' into an independent relation or can be kept inline.
Definition at line 357 of file MaterializeAggregationQueries.cpp.
|
inlineoverrideprivatevirtual |
Implements souffle::ast::transform::Transformer.
Definition at line 64 of file MaterializeAggregationQueries.h.