souffle  2.0.2-371-g6315b36
Provenance.cpp
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2017, 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 Provenance.cpp
12  *
13  * Implements Transformer for adding provenance information via extra columns
14  *
15  ***********************************************************************/
16 
18 #include "RelationTag.h"
19 #include "ast/Aggregator.h"
20 #include "ast/Argument.h"
21 #include "ast/Atom.h"
22 #include "ast/Attribute.h"
23 #include "ast/BinaryConstraint.h"
24 #include "ast/Clause.h"
25 #include "ast/Constant.h"
26 #include "ast/Functor.h"
27 #include "ast/IntrinsicFunctor.h"
28 #include "ast/Literal.h"
29 #include "ast/Negation.h"
30 #include "ast/Node.h"
31 #include "ast/NumericConstant.h"
32 #include "ast/Program.h"
33 #include "ast/ProvenanceNegation.h"
34 #include "ast/QualifiedName.h"
35 #include "ast/Relation.h"
36 #include "ast/StringConstant.h"
37 #include "ast/TranslationUnit.h"
38 #include "ast/UnnamedVariable.h"
39 #include "ast/Variable.h"
40 #include "ast/utility/NodeMapper.h"
41 #include "ast/utility/Utils.h"
48 #include <algorithm>
49 #include <cassert>
50 #include <cstddef>
51 #include <iosfwd>
52 #include <memory>
53 #include <string>
54 #include <utility>
55 #include <vector>
56 
57 namespace souffle::ast::transform {
58 
59 /**
60  * Helper functions
61  */
62 inline QualifiedName makeRelationName(const QualifiedName& orig, const std::string& type, int num = -1) {
63  QualifiedName newName(toString(orig));
64  newName.append(type);
65  if (num != -1) {
66  newName.append((const std::string&)std::to_string(num));
67  }
68 
69  return newName;
70 }
71 
73  Clause& originalClause, size_t originalClauseNum, TranslationUnit& translationUnit) {
74  QualifiedName name =
75  makeRelationName(originalClause.getHead()->getQualifiedName(), "@info", originalClauseNum);
76 
77  // initialise info relation
78  auto infoRelation = new Relation();
79  infoRelation->setQualifiedName(name);
80  // set qualifier to INFO_RELATION
81  infoRelation->setRepresentation(RelationRepresentation::INFO);
82 
83  // create new clause containing a single fact
84  auto infoClause = new Clause();
85  auto infoClauseHead = new Atom();
86  infoClauseHead->setQualifiedName(name);
87 
88  // (darth_tytus): Can this be unsigned?
89  infoRelation->addAttribute(mk<Attribute>("clause_num", QualifiedName("number")));
90  infoClauseHead->addArgument(mk<NumericConstant>(originalClauseNum));
91 
92  // add head relation as meta info
93  std::vector<std::string> headVariables;
94 
95  // a method to stringify an Argument, translating functors and aggregates
96  // keep a global counter of functor and aggregate numbers, which increment for each unique
97  // functor/aggregate
98  int functorNumber = 0;
99  int aggregateNumber = 0;
100  auto getArgInfo = [&](Argument* arg) -> std::string {
101  if (auto* var = dynamic_cast<ast::Variable*>(arg)) {
102  return toString(*var);
103  } else if (auto* constant = dynamic_cast<Constant*>(arg)) {
104  return toString(*constant);
105  }
106  if (isA<UnnamedVariable>(arg)) {
107  return "_";
108  }
109  if (isA<Functor>(arg)) {
110  return tfm::format("functor_%d", functorNumber++);
111  }
112  if (isA<Aggregator>(arg)) {
113  return tfm::format("agg_%d", aggregateNumber++);
114  }
115 
116  fatal("Unhandled argument type");
117  };
118 
119  // add head arguments
120  for (auto& arg : originalClause.getHead()->getArguments()) {
121  headVariables.push_back(getArgInfo(arg));
122  }
123 
124  // join variables in the head with commas
125  std::stringstream headVariableString;
126  headVariableString << join(headVariables, ",");
127 
128  // add an attribute to infoRelation for the head of clause
129  infoRelation->addAttribute(mk<Attribute>(std::string("head_vars"), QualifiedName("symbol")));
130  infoClauseHead->addArgument(mk<StringConstant>(toString(join(headVariables, ","))));
131 
132  // visit all body literals and add to info clause head
133  for (size_t i = 0; i < originalClause.getBodyLiterals().size(); i++) {
134  auto lit = originalClause.getBodyLiterals()[i];
135 
136  const Atom* atom = nullptr;
137  if (isA<Atom>(lit)) {
138  atom = static_cast<Atom*>(lit);
139  } else if (isA<Negation>(lit)) {
140  atom = static_cast<Negation*>(lit)->getAtom();
141  } else if (isA<ProvenanceNegation>(lit)) {
142  atom = static_cast<ProvenanceNegation*>(lit)->getAtom();
143  }
144 
145  // add an attribute for atoms and binary constraints
146  if (atom != nullptr || isA<BinaryConstraint>(lit)) {
147  infoRelation->addAttribute(
148  mk<Attribute>(std::string("rel_") + std::to_string(i), QualifiedName("symbol")));
149  }
150 
151  if (atom != nullptr) {
152  std::string relName = toString(atom->getQualifiedName());
153 
154  // for an atom, add its name and variables (converting aggregates to variables)
155  if (isA<Atom>(lit)) {
156  std::string atomDescription = relName;
157 
158  for (auto& arg : atom->getArguments()) {
159  atomDescription.append("," + getArgInfo(arg));
160  }
161 
162  infoClauseHead->addArgument(mk<StringConstant>(atomDescription));
163  // for a negation, add a marker with the relation name
164  } else if (isA<Negation>(lit)) {
165  infoClauseHead->addArgument(mk<StringConstant>("!" + relName));
166  }
167  }
168  }
169 
170  // visit all body constraints and add to info clause head
171  for (size_t i = 0; i < originalClause.getBodyLiterals().size(); i++) {
172  auto lit = originalClause.getBodyLiterals()[i];
173 
174  if (auto con = dynamic_cast<BinaryConstraint*>(lit)) {
175  // for a constraint, add the constraint symbol and LHS and RHS
176  std::string constraintDescription = toBinaryConstraintSymbol(con->getBaseOperator());
177 
178  constraintDescription.append("," + getArgInfo(con->getLHS()));
179  constraintDescription.append("," + getArgInfo(con->getRHS()));
180 
181  infoClauseHead->addArgument(mk<StringConstant>(constraintDescription));
182  }
183  }
184 
185  infoRelation->addAttribute(mk<Attribute>("clause_repr", QualifiedName("symbol")));
186  infoClauseHead->addArgument(mk<StringConstant>(toString(originalClause)));
187 
188  // set clause head and add clause to info relation
189  infoClause->setHead(Own<Atom>(infoClauseHead));
190  Program& program = translationUnit.getProgram();
191  program.addClause(Own<Clause>(infoClause));
192 
193  return Own<Relation>(infoRelation);
194 }
195 
196 /** Transform eqrel relations to explicitly define equivalence relations */
197 void transformEqrelRelation(Program& program, Relation& rel) {
198  assert(rel.getRepresentation() == RelationRepresentation::EQREL &&
199  "attempting to transform non-eqrel relation");
200  assert(rel.getArity() == 2 && "eqrel relation not binary");
201 
202  rel.setRepresentation(RelationRepresentation::BTREE);
203 
204  // transitivity
205  // transitive clause: A(x, z) :- A(x, y), A(y, z).
206  auto transitiveClause = new Clause();
207  auto transitiveClauseHead = new Atom(rel.getQualifiedName());
208  transitiveClauseHead->addArgument(mk<ast::Variable>("x"));
209  transitiveClauseHead->addArgument(mk<ast::Variable>("z"));
210 
211  auto transitiveClauseBody = new Atom(rel.getQualifiedName());
212  transitiveClauseBody->addArgument(mk<ast::Variable>("x"));
213  transitiveClauseBody->addArgument(mk<ast::Variable>("y"));
214 
215  auto transitiveClauseBody2 = new Atom(rel.getQualifiedName());
216  transitiveClauseBody2->addArgument(mk<ast::Variable>("y"));
217  transitiveClauseBody2->addArgument(mk<ast::Variable>("z"));
218 
219  transitiveClause->setHead(Own<Atom>(transitiveClauseHead));
220  transitiveClause->addToBody(Own<Literal>(transitiveClauseBody));
221  transitiveClause->addToBody(Own<Literal>(transitiveClauseBody2));
222  program.addClause(Own<Clause>(transitiveClause));
223 
224  // symmetric
225  // symmetric clause: A(x, y) :- A(y, x).
226  auto symClause = new Clause();
227  auto symClauseHead = new Atom(rel.getQualifiedName());
228  symClauseHead->addArgument(mk<ast::Variable>("x"));
229  symClauseHead->addArgument(mk<ast::Variable>("y"));
230 
231  auto symClauseBody = new Atom(rel.getQualifiedName());
232  symClauseBody->addArgument(mk<ast::Variable>("y"));
233  symClauseBody->addArgument(mk<ast::Variable>("x"));
234 
235  symClause->setHead(Own<Atom>(symClauseHead));
236  symClause->addToBody(Own<Literal>(symClauseBody));
237  program.addClause(Own<Clause>(symClause));
238 
239  // reflexivity
240  // reflexive clause: A(x, x) :- A(x, _).
241  auto reflexiveClause = new Clause();
242  auto reflexiveClauseHead = new Atom(rel.getQualifiedName());
243  reflexiveClauseHead->addArgument(mk<ast::Variable>("x"));
244  reflexiveClauseHead->addArgument(mk<ast::Variable>("x"));
245 
246  auto reflexiveClauseBody = new Atom(rel.getQualifiedName());
247  reflexiveClauseBody->addArgument(mk<ast::Variable>("x"));
248  reflexiveClauseBody->addArgument(mk<UnnamedVariable>());
249 
250  reflexiveClause->setHead(Own<Atom>(reflexiveClauseHead));
251  reflexiveClause->addToBody(Own<Literal>(reflexiveClauseBody));
252  program.addClause(Own<Clause>(reflexiveClause));
253 }
254 
255 namespace {
256 Own<Argument> getNextLevelNumber(const std::vector<Argument*>& levels) {
257  if (levels.empty()) return mk<NumericConstant>(0);
258 
259  auto max = levels.size() == 1
260  ? Own<Argument>(levels[0])
261  : mk<IntrinsicFunctor>("max", map(levels, [](auto&& x) { return Own<Argument>(x); }));
262 
263  return mk<IntrinsicFunctor>("+", std::move(max), mk<NumericConstant>(1));
264 }
265 } // namespace
266 
267 bool ProvenanceTransformer::transformMaxHeight(TranslationUnit& translationUnit) {
268  Program& program = translationUnit.getProgram();
269 
270  for (auto relation : program.getRelations()) {
271  if (relation->getRepresentation() == RelationRepresentation::EQREL) {
272  // Explicitly expand eqrel relation
274  }
275  }
276 
277  for (auto relation : program.getRelations()) {
278  // generate info relations for each clause
279  // do this before all other transformations so that we record
280  // the original rule without any instrumentation
281  for (auto clause : getClauses(program, *relation)) {
282  if (!isFact(*clause)) {
283  // add info relation
284  program.addRelation(
285  makeInfoRelation(*clause, getClauseNum(&program, clause), translationUnit));
286  }
287  }
288 
289  relation->addAttribute(mk<Attribute>(std::string("@rule_number"), QualifiedName("number")));
290  relation->addAttribute(mk<Attribute>(std::string("@level_number"), QualifiedName("number")));
291 
292  for (auto clause : getClauses(program, *relation)) {
293  size_t clauseNum = getClauseNum(&program, clause);
294 
295  // mapper to add two provenance columns to atoms
296  struct M : public NodeMapper {
297  using NodeMapper::operator();
298 
299  Own<Node> operator()(Own<Node> node) const override {
300  // add provenance columns
301  if (auto atom = dynamic_cast<Atom*>(node.get())) {
302  atom->addArgument(mk<UnnamedVariable>());
303  atom->addArgument(mk<UnnamedVariable>());
304  } else if (auto neg = dynamic_cast<Negation*>(node.get())) {
305  auto atom = neg->getAtom();
306  atom->addArgument(mk<UnnamedVariable>());
307  atom->addArgument(mk<UnnamedVariable>());
308  }
309 
310  // otherwise - apply mapper recursively
311  node->apply(*this);
312  return node;
313  }
314  };
315 
316  // add unnamed vars to each atom nested in arguments of head
317  clause->getHead()->apply(M());
318 
319  // if fact, level number is 0
320  if (isFact(*clause)) {
321  clause->getHead()->addArgument(mk<NumericConstant>(0));
322  clause->getHead()->addArgument(mk<NumericConstant>(0));
323  } else {
324  std::vector<Argument*> bodyLevels;
325 
326  for (size_t i = 0; i < clause->getBodyLiterals().size(); i++) {
327  auto lit = clause->getBodyLiterals()[i];
328 
329  // add unnamed vars to each atom nested in arguments of lit
330  lit->apply(M());
331 
332  // add two provenance columns to lit; first is rule num, second is level num
333  if (auto atom = dynamic_cast<Atom*>(lit)) {
334  atom->addArgument(mk<UnnamedVariable>());
335  atom->addArgument(mk<ast::Variable>("@level_num_" + std::to_string(i)));
336  bodyLevels.push_back(new ast::Variable("@level_num_" + std::to_string(i)));
337  }
338  }
339 
340  // add two provenance columns to head lit
341  clause->getHead()->addArgument(mk<NumericConstant>(clauseNum));
342  clause->getHead()->addArgument(getNextLevelNumber(bodyLevels));
343  }
344  }
345  }
346  return true;
347 }
348 
349 bool ProvenanceTransformer::transform(TranslationUnit& translationUnit) {
350  return ProvenanceTransformer::transformMaxHeight(translationUnit);
351 }
352 
353 } // namespace souffle::ast::transform
BinaryConstraintOps.h
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
ProvenanceNegation.h
TranslationUnit.h
Functor.h
souffle::ast::Atom::getArguments
std::vector< Argument * > getArguments() const
Return arguments.
Definition: Atom.h:77
UnnamedVariable.h
souffle::ast::transform::ProvenanceTransformer::transformMaxHeight
bool transformMaxHeight(TranslationUnit &translationUnit)
Definition: Provenance.cpp:273
tinyformat::format
void format(std::ostream &out, const char *fmt)
Definition: tinyformat.h:1089
souffle::RelationRepresentation::EQREL
@ EQREL
souffle::ast::getClauseNum
size_t getClauseNum(const Program *program, const Clause *clause)
Returns the index of a clause within its relation, ignoring facts.
Definition: Utils.cpp:150
souffle::Own
std::unique_ptr< A > Own
Definition: ContainerUtil.h:42
relation
Relation & relation
Definition: Reader.h:130
souffle::map
auto map(const std::vector< A > &xs, F &&f)
Applies a function to each element of a vector and returns the results.
Definition: ContainerUtil.h:158
MiscUtil.h
souffle::ast::Relation
Defines a relation with a name, attributes, qualifiers, and internal representation.
Definition: Relation.h:49
souffle::ast::Clause
Intermediate representation of a horn clause.
Definition: Clause.h:51
souffle::ast::isFact
bool isFact(const Clause &clause)
Returns whether the given clause is a fact.
Definition: Utils.cpp:214
souffle::toBinaryConstraintSymbol
char const * toBinaryConstraintSymbol(const BinaryConstraintOp op)
Converts operator to its symbolic representation.
Definition: BinaryConstraintOps.h:336
souffle::ast::analysis::Variable
A variable to be utilized within constraints to be handled by the constraint solver.
Definition: ConstraintSystem.h:41
Relation.h
IntrinsicFunctor.h
tinyformat.h
souffle::ast::Atom
An atom class.
Definition: Atom.h:51
souffle::ast::Clause::getHead
Atom * getHead() const
Return the atom that represents the head of the clause.
Definition: Clause.h:74
Utils.h
NodeMapper.h
Constant.h
souffle::ast::Argument
An abstract class for arguments.
Definition: Argument.h:33
Attribute.h
souffle::toString
const std::string & toString(const std::string &str)
A generic function converting strings into strings (trivial case).
Definition: StringUtil.h:234
StringConstant.h
Argument.h
souffle::ast::Atom::getQualifiedName
const QualifiedName & getQualifiedName() const
Return qualified name.
Definition: Atom.h:57
souffle::mk
Own< A > mk(Args &&... xs)
Definition: ContainerUtil.h:48
i
size_t i
Definition: json11.h:663
ContainerUtil.h
Provenance.h
RelationTag.h
souffle::join
detail::joined_sequence< Iter, Printer > join(const Iter &a, const Iter &b, const std::string &sep, const Printer &p)
Creates an object to be forwarded to some output stream for printing sequences of elements interspers...
Definition: StreamUtil.h:175
StringUtil.h
souffle::ast::IntrinsicFunctor
Intrinsic Functor class for functors are in-built.
Definition: IntrinsicFunctor.h:47
Atom.h
souffle::ast::Constant
Abstract constant class.
Definition: Constant.h:38
Literal.h
souffle::ast::TranslationUnit
Translation unit class for the translation pipeline.
Definition: TranslationUnit.h:51
souffle::ast::Program::addClause
void addClause(Own< Clause > clause)
Add a clause.
Definition: Program.cpp:62
souffle::ast::transform
Definition: Program.h:45
Negation.h
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
souffle::ast::transform::transformEqrelRelation
void transformEqrelRelation(Program &program, Relation &rel)
Transform eqrel relations to explicitly define equivalence relations.
Definition: Provenance.cpp:203
Aggregator.h
QualifiedName.h
Program.h
StreamUtil.h
souffle::ast::transform::makeRelationName
QualifiedName makeRelationName(const QualifiedName &orig, const std::string &type, int num=-1)
Helper functions.
Definition: Provenance.cpp:68
souffle::fatal
void fatal(const char *format, const Args &... args)
Definition: MiscUtil.h:198
souffle::RelationRepresentation::INFO
@ INFO
Clause.h
souffle::ast::transform::makeInfoRelation
Own< Relation > makeInfoRelation(Clause &originalClause, size_t originalClauseNum, TranslationUnit &translationUnit)
Definition: Provenance.cpp:78
BinaryConstraint.h
souffle::RelationRepresentation::BTREE
@ BTREE
souffle::ast::QualifiedName
Qualified Name class defines fully/partially qualified names to identify objects in components.
Definition: QualifiedName.h:39
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::ast::TranslationUnit::getProgram
Program & getProgram() const
Return the program.
Definition: TranslationUnit.h:84
souffle::ast::transform::ProvenanceTransformer::transform
bool transform(TranslationUnit &translationUnit) override
Definition: Provenance.cpp:355
NumericConstant.h
std::type
ElementType type
Definition: span.h:640
souffle::ast::Clause::getBodyLiterals
std::vector< Literal * > getBodyLiterals() const
Obtains a copy of the internally maintained body literals.
Definition: Clause.h:79
Variable.h