souffle  2.0.2-371-g6315b36
Public Member Functions | Static Public Member Functions | Private Member Functions | Static Private Member Functions
souffle::ast::transform::MinimiseProgramTransformer Class Reference

Transformation pass to remove equivalent rules. More...

#include <MinimiseProgram.h>

Inheritance diagram for souffle::ast::transform::MinimiseProgramTransformer:
Inheritance graph
Collaboration diagram for souffle::ast::transform::MinimiseProgramTransformer:
Collaboration graph

Public Member Functions

MinimiseProgramTransformerclone () 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 areBijectivelyEquivalent (const analysis::NormalisedClause &left, const analysis::NormalisedClause &right)
 

Private Member Functions

bool transform (TranslationUnit &translationUnit) override
 

Static Private Member Functions

static bool existsValidPermutation (const analysis::NormalisedClause &left, const analysis::NormalisedClause &right, const std::vector< std::vector< unsigned int >> &permutationMatrix)
 
static bool isValidPermutation (const analysis::NormalisedClause &left, const analysis::NormalisedClause &right, const std::vector< unsigned int > &permutation)
 – Bijective Equivalence Helper Methods – More...
 
static bool reduceClauseBodies (TranslationUnit &translationUnit)
 Remove redundant literals within a clause. More...
 
static bool reduceLocallyEquivalentClauses (TranslationUnit &translationUnit)
 – Sub-Transformations – More...
 
static bool reduceSingletonRelations (TranslationUnit &translationUnit)
 Removes redundant singleton relations. More...
 
static bool removeRedundantClauses (TranslationUnit &translationUnit)
 Remove clauses that are only satisfied if they are already satisfied. More...
 

Detailed Description

Transformation pass to remove equivalent rules.

Definition at line 36 of file MinimiseProgram.h.

Member Function Documentation

◆ areBijectivelyEquivalent()

bool souffle::ast::transform::MinimiseProgramTransformer::areBijectivelyEquivalent ( const analysis::NormalisedClause left,
const analysis::NormalisedClause right 
)
static

Definition at line 190 of file MinimiseProgram.cpp.

193  {
194  return false;
195  }
196 
197  // rules must be the same length to be equal
198  if (leftElements.size() != rightElements.size()) {
199  return false;
200  }
201 
202  // head atoms must have the same arity (names do not matter)
203  if (leftElements[0].params.size() != rightElements[0].params.size()) {
204  return false;
205  }
206 
207  // rules must have the same number of distinct variables
208  if (leftVars.size() != rightVars.size()) {
209  return false;
210  }
211 
212  // rules must have the exact same set of constants
213  if (left.getConstants() != right.getConstants()) {
214  return false;
215  }
216 
217  // set up the n x n permutation matrix, where n is the number of clause elements
218  size_t size = leftElements.size();
219  auto permutationMatrix = std::vector<std::vector<unsigned int>>(size);
220  for (auto& i : permutationMatrix) {
221  i = std::vector<unsigned int>(size);
222  }
223 
224  // create permutation matrix
225  for (size_t i = 0; i < size; i++) {
226  for (size_t j = 0; j < size; j++) {
227  if (leftElements[i].name == rightElements[j].name) {
228  permutationMatrix[i][j] = 1;
229  }
230  }
231  }
232 
233  // check if any of these permutations have valid variable mappings
234  return existsValidPermutation(left, right, permutationMatrix);
235 }
236 
237 bool MinimiseProgramTransformer::reduceLocallyEquivalentClauses(TranslationUnit& translationUnit) {
238  Program& program = translationUnit.getProgram();
239  const auto& normalisations = *translationUnit.getAnalysis<analysis::ClauseNormalisationAnalysis>();
240 
241  std::vector<Clause*> clausesToDelete;

◆ clone()

MinimiseProgramTransformer* souffle::ast::transform::MinimiseProgramTransformer::clone ( ) const
inlineoverridevirtual

Implements souffle::ast::transform::Transformer.

Definition at line 52 of file MinimiseProgram.h.

◆ existsValidPermutation()

bool souffle::ast::transform::MinimiseProgramTransformer::existsValidPermutation ( const analysis::NormalisedClause left,
const analysis::NormalisedClause right,
const std::vector< std::vector< unsigned int >> &  permutationMatrix 
)
staticprivate

Definition at line 53 of file MinimiseProgram.cpp.

54  {
55  if (permutationMatrix[i][j] == 1) {
56  currentRow.push_back(j);
57  }
58  }
59  validMoves.push_back(currentRow);
60  }
61 
62  // extract the possible permutations, DFS style
63  std::vector<unsigned int> seen(clauseSize);
64  std::vector<unsigned int> currentPermutation;
65  std::stack<std::vector<unsigned int>> todoStack;
66 
67  todoStack.push(validMoves[0]);
68 
69  size_t currentIdx = 0;
70  while (!todoStack.empty()) {
71  if (currentIdx == clauseSize) {
72  // permutation is complete, check if it's valid
73  if (isValidPermutation(left, right, currentPermutation)) {
74  // valid permutation with valid mapping
75  // therefore, the two clauses are equivalent!
76  return true;
77  }
78 
79  // Not valid yet, keep checking for more permutations
80  if (currentIdx == 0) {
81  // already at starting position, so no more permutations possible
82  break;
83  }
84 
85  // undo the last number added to the permutation
86  currentIdx--;
87  seen[currentPermutation[currentIdx]] = 0;
88  currentPermutation.pop_back();
89 
90  // see if we can pick up other permutations
91  continue;
92  }
93 
94  // pull out the possibilities for the current point of the permutation
95  std::vector<unsigned int> possibilities = todoStack.top();
96  todoStack.pop();
97  if (possibilities.empty()) {
98  // no more possibilities at this point, so undo our last move
99 
100  if (currentIdx == 0) {
101  // already at starting position, so no more permutations possible
102  break;
103  }
104 
105  currentIdx--;
106  seen[currentPermutation[currentIdx]] = 0;
107  currentPermutation.pop_back();
108 
109  // continue looking for permutations
110  continue;
111  }
112 
113  // try the next possibility
114  unsigned int nextNum = possibilities[0];
115 
116  // update the possibility vector for the current position
117  possibilities.erase(possibilities.begin());
118  todoStack.push(possibilities);
119 
120  if (seen[nextNum] != 0u) {
121  // number already seen in this permutation
122  continue;
123  } else {
124  // number can be used
125  seen[nextNum] = 1;
126  currentPermutation.push_back(nextNum);
127  currentIdx++;
128 
129  // if we havent reached the end of the permutation,
130  // push up the valid moves for the next position
131  if (currentIdx < clauseSize) {
132  todoStack.push(validMoves[currentIdx]);
133  }
134  }
135  }
136 
137  // checked all permutations, none were valid
138  return false;
139 }
140 
141 bool MinimiseProgramTransformer::isValidPermutation(const NormalisedClause& left,
142  const NormalisedClause& right, const std::vector<unsigned int>& permutation) {
143  const auto& leftElements = left.getElements();
144  const auto& rightElements = right.getElements();
145 

References i, and j.

◆ getName()

std::string souffle::ast::transform::MinimiseProgramTransformer::getName ( ) const
inlineoverridevirtual

Implements souffle::ast::transform::Transformer.

Definition at line 44 of file MinimiseProgram.h.

44  :
45  bool transform(TranslationUnit& translationUnit) override;
46 

◆ isValidPermutation()

bool souffle::ast::transform::MinimiseProgramTransformer::isValidPermutation ( const analysis::NormalisedClause left,
const analysis::NormalisedClause right,
const std::vector< unsigned int > &  permutation 
)
staticprivate

– Bijective Equivalence Helper Methods –

Definition at line 147 of file MinimiseProgram.cpp.

152  : left.getConstants()) {
153  variableMap[cst] = cst;
154  }
155 
156  // Variables start off mapping to nothing
157  for (const auto& var : left.getVariables()) {
158  variableMap[var] = "";
159  }
160 
161  // Pass through the all arguments in the first clause in sequence, mapping each to the corresponding
162  // argument in the second clause under the literal permutation
163  for (size_t i = 0; i < size; i++) {
164  const auto& leftArgs = leftElements[i].params;
165  const auto& rightArgs = rightElements[permutation[i]].params;
166  for (size_t j = 0; j < leftArgs.size(); j++) {
167  auto leftArg = leftArgs[j];
168  auto rightArg = rightArgs[j];
169  std::string currentMap = variableMap[leftArg];
170  if (currentMap.empty()) {
171  // unassigned yet, so assign it appropriately
172  variableMap[leftArg] = rightArg;
173  } else if (currentMap != rightArg) {
174  // inconsistent mapping!
175  // clauses cannot be equivalent under this permutation
176  return false;
177  }
178  }
179  }
180 
181  return true;
182 }
183 
185  const NormalisedClause& left, const NormalisedClause& right) {
186  const auto& leftElements = left.getElements();
187  const auto& rightElements = right.getElements();
188 

◆ reduceClauseBodies()

bool souffle::ast::transform::MinimiseProgramTransformer::reduceClauseBodies ( TranslationUnit translationUnit)
staticprivate

Remove redundant literals within a clause.

Definition at line 394 of file MinimiseProgram.cpp.

396  {
397  for (size_t j = 0; j < i; j++) {
398  if (*bodyLiterals[i] == *bodyLiterals[j]) {
399  redundantPositions.insert(j);
400  break;
401  }
402  }
403  }
404 
405  if (!redundantPositions.empty()) {
406  auto minimisedClause = mk<Clause>();
407  minimisedClause->setHead(souffle::clone(clause->getHead()));
408  for (size_t i = 0; i < bodyLiterals.size(); i++) {
409  if (!contains(redundantPositions, i)) {
410  minimisedClause->addToBody(souffle::clone(bodyLiterals[i]));
411  }
412  }
413  clausesToAdd.insert(std::move(minimisedClause));
414  clausesToRemove.insert(souffle::clone(clause));
415  }
416  }
417 
418  for (auto& clause : clausesToRemove) {
419  program.removeClause(clause.get());
420  }
421  for (auto& clause : clausesToAdd) {
422  program.addClause(souffle::clone(clause));
423  }
424 
425  return !clausesToAdd.empty();
426 }
427 
428 bool MinimiseProgramTransformer::transform(TranslationUnit& translationUnit) {
429  bool changed = false;
430  changed |= reduceClauseBodies(translationUnit);
431  if (changed) translationUnit.invalidateAnalyses();
432  changed |= removeRedundantClauses(translationUnit);

References i, and j.

◆ reduceLocallyEquivalentClauses()

bool souffle::ast::transform::MinimiseProgramTransformer::reduceLocallyEquivalentClauses ( TranslationUnit translationUnit)
staticprivate

– Sub-Transformations –

Reduces locally-redundant clauses. A clause is locally-redundant if there is another clause within the same relation that computes the same set of tuples.

Definition at line 243 of file MinimiseProgram.cpp.

245  : program.getRelations()) {
246  std::vector<std::vector<Clause*>> equivalenceClasses;
247 
248  for (Clause* clause : getClauses(program, *rel)) {
249  bool added = false;
250 
251  for (std::vector<Clause*>& eqClass : equivalenceClasses) {
252  const auto& normedRep = normalisations.getNormalisation(eqClass[0]);
253  const auto& normedClause = normalisations.getNormalisation(clause);
254  if (areBijectivelyEquivalent(normedRep, normedClause)) {
255  // clause belongs to an existing equivalence class, so delete it
256  eqClass.push_back(clause);
257  clausesToDelete.push_back(clause);
258  added = true;
259  break;
260  }
261  }
262 
263  if (!added) {
264  // clause does not belong to any existing equivalence class, so keep it
265  std::vector<Clause*> clauseToAdd = {clause};
266  equivalenceClasses.push_back(clauseToAdd);
267  }
268  }
269  }
270 
271  // remove non-representative clauses
272  for (auto clause : clausesToDelete) {
273  program.removeClause(clause);
274  }
275 
276  // changed iff any clauses were deleted
277  return !clausesToDelete.empty();
278 }
279 
280 bool MinimiseProgramTransformer::reduceSingletonRelations(TranslationUnit& translationUnit) {
281  // Note: This reduction is particularly useful in conjunction with the
282  // body-partitioning transformation
283  Program& program = translationUnit.getProgram();
284  const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();

References souffle::ast::getClauses(), and rel().

Here is the call graph for this function:

◆ reduceSingletonRelations()

bool souffle::ast::transform::MinimiseProgramTransformer::reduceSingletonRelations ( TranslationUnit translationUnit)
staticprivate

Removes redundant singleton relations.

Singleton relations are relations with a single clause. A singleton relation is redundant if there exists another singleton relation that computes the same set of tuples.

Returns
true iff the program was changed

Definition at line 286 of file MinimiseProgram.cpp.

289  : program.getRelations()) {
290  if (!ioTypes.isIO(rel) && getClauses(program, *rel).size() == 1) {
291  Clause* clause = getClauses(program, *rel)[0];
292  singletonRelationClauses.push_back(clause);
293  }
294  }
295 
296  // Keep track of clauses found to be redundant
297  std::set<const Clause*> redundantClauses;
298 
299  // Keep track of canonical relation name for each redundant clause
300  std::map<QualifiedName, QualifiedName> canonicalName;
301 
302  // Check pairwise equivalence of each singleton relation
303  for (size_t i = 0; i < singletonRelationClauses.size(); i++) {
304  const auto* first = singletonRelationClauses[i];
305  if (redundantClauses.find(first) != redundantClauses.end()) {
306  // Already found to be redundant, no need to check
307  continue;
308  }
309 
310  for (size_t j = i + 1; j < singletonRelationClauses.size(); j++) {
311  const auto* second = singletonRelationClauses[j];
312 
313  // Note: Bijective-equivalence check does not care about the head relation name
314  const auto& normedFirst = normalisations.getNormalisation(first);
315  const auto& normedSecond = normalisations.getNormalisation(second);
316  if (areBijectivelyEquivalent(normedFirst, normedSecond)) {
317  QualifiedName firstName = first->getHead()->getQualifiedName();
318  QualifiedName secondName = second->getHead()->getQualifiedName();
319  redundantClauses.insert(second);
320  canonicalName.insert(std::pair(secondName, firstName));
321  }
322  }
323  }
324 
325  // Remove redundant relation definitions
326  for (const auto* clause : redundantClauses) {
327  auto relName = clause->getHead()->getQualifiedName();
328  Relation* rel = getRelation(program, relName);
329  assert(rel != nullptr && "relation does not exist in program");
330  removeRelation(translationUnit, relName);
331  }
332 
333  // Replace each redundant relation appearance with its canonical name
334  struct replaceRedundantRelations : public NodeMapper {
335  const std::map<QualifiedName, QualifiedName>& canonicalName;
336 
337  replaceRedundantRelations(const std::map<QualifiedName, QualifiedName>& canonicalName)
338  : canonicalName(canonicalName) {}
339 
340  Own<Node> operator()(Own<Node> node) const override {
341  // Remove appearances from children nodes
342  node->apply(*this);
343 
344  if (auto* atom = dynamic_cast<Atom*>(node.get())) {
345  auto pos = canonicalName.find(atom->getQualifiedName());
346  if (pos != canonicalName.end()) {
347  auto newAtom = souffle::clone(atom);
348  newAtom->setQualifiedName(pos->second);
349  return newAtom;
350  }
351  }
352 
353  return node;
354  }
355  };
356  replaceRedundantRelations update(canonicalName);
357  program.apply(update);
358 
359  // Program was changed iff a relation was replaced
360  return !canonicalName.empty();
361 }
362 
363 bool MinimiseProgramTransformer::removeRedundantClauses(TranslationUnit& translationUnit) {
364  Program& program = translationUnit.getProgram();
365  auto isRedundant = [&](const Clause* clause) {
366  const auto* head = clause->getHead();
367  for (const auto* lit : clause->getBodyLiterals()) {

References souffle::ast::getClauses(), and rel().

Here is the call graph for this function:

◆ removeRedundantClauses()

bool souffle::ast::transform::MinimiseProgramTransformer::removeRedundantClauses ( TranslationUnit translationUnit)
staticprivate

Remove clauses that are only satisfied if they are already satisfied.

Definition at line 369 of file MinimiseProgram.cpp.

376  : program.getClauses()) {
377  if (isRedundant(clause)) {
378  clausesToRemove.insert(souffle::clone(clause));
379  }
380  }
381 
382  for (auto& clause : clausesToRemove) {
383  program.removeClause(clause.get());
384  }
385  return !clausesToRemove.empty();
386 }
387 
388 bool MinimiseProgramTransformer::reduceClauseBodies(TranslationUnit& translationUnit) {
389  Program& program = translationUnit.getProgram();
390  std::set<Own<Clause>> clausesToAdd;
391  std::set<Own<Clause>> clausesToRemove;
392 

◆ transform()

bool souffle::ast::transform::MinimiseProgramTransformer::transform ( TranslationUnit translationUnit)
overrideprivatevirtual

Implements souffle::ast::transform::Transformer.

Definition at line 434 of file MinimiseProgram.cpp.


The documentation for this class was generated from the following files:
souffle::ast::transform::MinimiseProgramTransformer::removeRedundantClauses
static bool removeRedundantClauses(TranslationUnit &translationUnit)
Remove clauses that are only satisfied if they are already satisfied.
Definition: MinimiseProgram.cpp:369
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
souffle::ast::removeRelation
void removeRelation(TranslationUnit &tu, const QualifiedName &name)
Remove relation and all its clauses from the program.
Definition: Utils.cpp:105
souffle::ast::transform::MinimiseProgramTransformer::reduceClauseBodies
static bool reduceClauseBodies(TranslationUnit &translationUnit)
Remove redundant literals within a clause.
Definition: MinimiseProgram.cpp:394
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::transform::MinimiseProgramTransformer::transform
bool transform(TranslationUnit &translationUnit) override
Definition: MinimiseProgram.cpp:434
j
var j
Definition: htmlJsChartistMin.h:15
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
i
size_t i
Definition: json11.h:663
souffle::ast::getRelation
Relation * getRelation(const Program &program, const QualifiedName &name)
Returns the relation with the given name in the program.
Definition: Utils.cpp:101
souffle::ast::transform::MinimiseProgramTransformer::reduceLocallyEquivalentClauses
static bool reduceLocallyEquivalentClauses(TranslationUnit &translationUnit)
– Sub-Transformations –
Definition: MinimiseProgram.cpp:243
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
souffle::ast::transform::MinimiseProgramTransformer::isValidPermutation
static bool isValidPermutation(const analysis::NormalisedClause &left, const analysis::NormalisedClause &right, const std::vector< unsigned int > &permutation)
– Bijective Equivalence Helper Methods –
Definition: MinimiseProgram.cpp:147
souffle::ast::transform::MinimiseProgramTransformer::areBijectivelyEquivalent
static bool areBijectivelyEquivalent(const analysis::NormalisedClause &left, const analysis::NormalisedClause &right)
Definition: MinimiseProgram.cpp:190
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
souffle::ast::transform::MinimiseProgramTransformer::existsValidPermutation
static bool existsValidPermutation(const analysis::NormalisedClause &left, const analysis::NormalisedClause &right, const std::vector< std::vector< unsigned int >> &permutationMatrix)
Definition: MinimiseProgram.cpp:53
souffle::ast::transform::MinimiseProgramTransformer::reduceSingletonRelations
static bool reduceSingletonRelations(TranslationUnit &translationUnit)
Removes redundant singleton relations.
Definition: MinimiseProgram.cpp:286