67 std::set<QualifiedName> triviallyIgnoredRelations;
70 for (
auto*
rel : program.getRelations()) {
72 if (ioTypes.isInput(
rel)) {
73 triviallyIgnoredRelations.insert(
rel->getQualifiedName());
78 bool hasRules =
false;
79 for (
const auto* clause :
getClauses(program,
rel->getQualifiedName())) {
83 triviallyIgnoredRelations.insert(
rel->getQualifiedName());
87 return triviallyIgnoredRelations;
94 std::set<QualifiedName> weaklyIgnoredRelations;
97 std::vector<QualifiedName> specifiedRelations;
101 for (
const auto& relStr : configRels) {
102 std::vector<std::string> qualifiers =
splitString(relStr,
'.');
107 for (
const auto*
rel : program.getRelations()) {
109 specifiedRelations.push_back(
rel->getQualifiedName());
115 for (
const Relation*
rel : program.getRelations()) {
116 if (!
contains(specifiedRelations,
rel->getQualifiedName())) {
117 weaklyIgnoredRelations.insert(
rel->getQualifiedName());
124 weaklyIgnoredRelations.insert(relName);
129 const auto& qualifiers = atom.getQualifiedName().getQualifiers();
130 if (!qualifiers.empty() && qualifiers[0] ==
"@neglabel") {
131 weaklyIgnoredRelations.insert(atom.getQualifiedName());
136 const std::set<BinaryConstraintOp> floatOps(
139 for (
const auto* clause : program.getClauses()) {
141 if (
contains(floatOps, polyAnalysis.getOverloadedOperator(&bc))) {
142 weaklyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
148 const std::set<FunctorOp> orderDepFuncOps(
150 for (
const auto* clause : program.getClauses()) {
152 if (
contains(orderDepFuncOps, polyAnalysis.getOverloadedFunctionOp(&functor))) {
153 weaklyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
159 for (
auto*
rel : program.getRelations()) {
161 weaklyIgnoredRelations.insert(
rel->getQualifiedName());
166 for (
auto* clause : program.getClauses()) {
167 if (clause->getExecutionPlan() !=
nullptr) {
168 weaklyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
173 for (
auto* clause : program.getClauses()) {
174 bool containsCounter =
false;
175 visitDepthFirst(*clause, [&](
const Counter& ) { containsCounter =
true; });
176 if (containsCounter) {
178 [&](
const Atom& atom) { weaklyIgnoredRelations.insert(atom.getQualifiedName()); });
186 for (
const auto& relName : stronglyIgnoredRelations) {
187 weaklyIgnoredRelations.insert(relName);
194 for (
const auto& relName : stronglyIgnoredRelations) {
195 precedenceGraph.visitDepthFirst(
getRelation(program, relName), [&](
const auto* dependentRel) {
197 for (
const auto* clause : program.getClauses()) {
198 const auto& atoms = getBodyLiterals<Atom>(*clause);
199 bool startIgnoring = false;
200 for (const auto& atom : atoms) {
201 startIgnoring |= (atom->getQualifiedName() == depName);
203 weaklyIgnoredRelations.insert(atom->getQualifiedName());
210 return weaklyIgnoredRelations;
213 std::set<QualifiedName> MagicSetTransformer::getStronglyIgnoredRelations(
const TranslationUnit& tu) {
214 const auto& program = tu.getProgram();
215 const auto& relDetail = *tu.getAnalysis<analysis::RelationDetailCacheAnalysis>();
216 const auto& precedenceGraph = tu.getAnalysis<analysis::PrecedenceGraphAnalysis>()->graph();
217 std::set<QualifiedName> stronglyIgnoredRelations;
220 for (
const auto* clause : program.getClauses()) {
221 bool containsCounter =
false;
223 if (containsCounter) {
224 stronglyIgnoredRelations.insert(clause->getHead()->getQualifiedName());
228 bool fixpointReached =
false;
229 while (!fixpointReached) {
230 fixpointReached =
true;
232 std::set<QualifiedName> dependentRelations;
233 for (
const auto& relName : stronglyIgnoredRelations) {
234 precedenceGraph.visitDepthFirst(
getRelation(program, relName), [&](
const auto* dependentRel) {
235 dependentRelations.insert(dependentRel->getQualifiedName());
238 for (
const auto& depRel : dependentRelations) {
239 if (!
contains(stronglyIgnoredRelations, depRel)) {
240 fixpointReached =
false;
241 stronglyIgnoredRelations.insert(depRel);
246 std::set<QualifiedName> bodyRelations;
247 for (
const auto& relName : stronglyIgnoredRelations) {
248 for (
const auto* clause : relDetail.getClauses(relName)) {
250 *clause, [&](
const Atom& atom) { bodyRelations.insert(atom.getQualifiedName()); });
253 for (
const auto& bodyRel : bodyRelations) {
254 if (!
contains(stronglyIgnoredRelations, bodyRel)) {
255 fixpointReached =
false;
256 stronglyIgnoredRelations.insert(bodyRel);
261 return stronglyIgnoredRelations;
264 std::set<QualifiedName> MagicSetTransformer::getRelationsToNotLabel(
const TranslationUnit& tu) {
265 std::set<QualifiedName> result;
266 for (
const auto& name : getTriviallyIgnoredRelations(tu)) {
269 for (
const auto& name : getStronglyIgnoredRelations(tu)) {
284 bool NormaliseDatabaseTransformer::transform(
TranslationUnit& translationUnit) {
285 bool changed =
false;
288 changed |= partitionIO(translationUnit);
292 changed |= extractIDB(translationUnit);
296 changed |= normaliseArguments(translationUnit);
300 changed |= querifyOutputRelations(translationUnit);
306 bool NormaliseDatabaseTransformer::partitionIO(
TranslationUnit& translationUnit) {
311 std::set<QualifiedName> relationsToSplit;
313 if (ioTypes.isInput(
rel) && (ioTypes.isOutput(
rel) || ioTypes.isPrintSize(
rel))) {
314 relationsToSplit.insert(
rel->getQualifiedName());
320 for (
auto relName : relationsToSplit) {
322 assert(
rel !=
nullptr &&
"relation does not exist");
324 newRelName.prepend(
"@split_in");
327 auto newRelation = mk<Relation>(newRelName);
328 for (
const auto* attr :
rel->getAttributes()) {
333 auto newClause = mk<Clause>();
334 auto newHeadAtom = mk<Atom>(relName);
335 auto newBodyAtom = mk<Atom>(newRelName);
336 for (
size_t i = 0;
i <
rel->getArity();
i++) {
337 std::stringstream varName;
338 varName <<
"@var" <<
i;
339 newHeadAtom->addArgument(mk<ast::Variable>(varName.str()));
340 newBodyAtom->addArgument(mk<ast::Variable>(varName.str()));
342 newClause->setHead(std::move(newHeadAtom));
343 newClause->addToBody(std::move(newBodyAtom));
346 std::set<const Directive*> iosToDelete;
347 std::set<Own<Directive>> iosToAdd;
352 newIO->setQualifiedName(newRelName);
353 iosToAdd.insert(std::move(newIO));
356 iosToDelete.insert(io);
359 for (
const auto* io : iosToDelete) {
362 for (
auto& io : iosToAdd) {
371 return !relationsToSplit.empty();
374 bool NormaliseDatabaseTransformer::extractIDB(TranslationUnit& translationUnit) {
375 Program& program = translationUnit.getProgram();
376 const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
379 auto isStrictlyEDB = [&](
const Relation*
rel) {
380 bool hasRules =
false;
381 for (
const auto* clause :
getClauses(program,
rel->getQualifiedName())) {
388 std::set<QualifiedName> inputRelationNames;
389 for (
auto*
rel : program.getRelations()) {
390 if (ioTypes.isInput(
rel) && !isStrictlyEDB(
rel)) {
391 assert(!ioTypes.isOutput(
rel) && !ioTypes.isPrintSize(
rel) &&
392 "input relations should not be output at this stage");
393 inputRelationNames.insert(
rel->getQualifiedName());
399 std::map<QualifiedName, QualifiedName> inputToIntermediate;
400 for (
const auto& inputRelationName : inputRelationNames) {
402 QualifiedName intermediateName(inputRelationName);
403 intermediateName.prepend(
"@interm_in");
404 inputToIntermediate[inputRelationName] = intermediateName;
408 intermediateRelation->setQualifiedName(intermediateName);
409 program.addRelation(std::move(intermediateRelation));
416 for (
const auto& inputRelationName : inputRelationNames) {
417 auto queryHead = mk<Atom>(inputToIntermediate.at(inputRelationName));
418 auto queryLiteral = mk<Atom>(inputRelationName);
421 const auto* inputRelation =
getRelation(program, inputRelationName);
422 for (
size_t i = 0;
i < inputRelation->getArity();
i++) {
423 std::stringstream var;
424 var <<
"@query_x" <<
i;
425 queryHead->addArgument(mk<ast::Variable>(var.str()));
426 queryLiteral->addArgument(mk<ast::Variable>(var.str()));
429 auto query = mk<Clause>(std::move(queryHead));
430 query->addToBody(std::move(queryLiteral));
431 program.addClause(std::move(query));
434 return !inputRelationNames.empty();
437 bool NormaliseDatabaseTransformer::querifyOutputRelations(TranslationUnit& translationUnit) {
438 Program& program = translationUnit.getProgram();
441 auto isStrictlyOutput = [&](
const Relation*
rel) {
442 bool strictlyOutput =
true;
443 size_t ruleCount = 0;
445 for (
const auto* clause : program.getClauses()) {
448 if (atom.getQualifiedName() == rel->getQualifiedName()) {
449 strictlyOutput = false;
454 if (clause->getHead()->getQualifiedName() ==
rel->getQualifiedName()) {
459 return strictlyOutput && ruleCount <= 1;
463 const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
464 std::set<QualifiedName> outputRelationNames;
465 for (
auto*
rel : program.getRelations()) {
466 if ((ioTypes.isOutput(
rel) || ioTypes.isPrintSize(
rel)) && !isStrictlyOutput(
rel)) {
467 assert(!ioTypes.isInput(
rel) &&
"output relations should not be input at this stage");
468 outputRelationNames.insert(
rel->getQualifiedName());
474 std::map<QualifiedName, QualifiedName> outputToIntermediate;
475 for (
const auto& outputRelationName : outputRelationNames) {
477 QualifiedName intermediateName(outputRelationName);
478 intermediateName.prepend(
"@interm_out");
479 outputToIntermediate[outputRelationName] = intermediateName;
483 intermediateRelation->setQualifiedName(intermediateName);
484 program.addRelation(std::move(intermediateRelation));
491 for (
const auto& outputRelationName : outputRelationNames) {
492 auto queryHead = mk<Atom>(outputRelationName);
493 auto queryLiteral = mk<Atom>(outputToIntermediate.at(outputRelationName));
496 const auto* outputRelation =
getRelation(program, outputRelationName);
497 for (
size_t i = 0;
i < outputRelation->getArity();
i++) {
498 std::stringstream var;
499 var <<
"@query_x" <<
i;
500 queryHead->addArgument(mk<ast::Variable>(var.str()));
501 queryLiteral->addArgument(mk<ast::Variable>(var.str()));
503 auto query = mk<Clause>(std::move(queryHead));
504 query->addToBody(std::move(queryLiteral));
505 program.addClause(std::move(query));
508 return !outputRelationNames.empty();
511 bool NormaliseDatabaseTransformer::normaliseArguments(TranslationUnit& translationUnit) {
512 Program& program = translationUnit.getProgram();
516 struct argument_normaliser :
public NodeMapper {
517 std::set<Own<BinaryConstraint>>& constraints;
521 : constraints(constraints), changeCount(changeCount) {}
524 if (
auto* aggr =
dynamic_cast<Aggregator*
>(node.get())) {
527 std::set<Own<BinaryConstraint>> subConstraints;
528 argument_normaliser aggrUpdate(subConstraints, changeCount);
529 aggr->apply(aggrUpdate);
532 std::vector<Own<Literal>> newBodyLiterals;
533 for (
const auto* lit : aggr->getBodyLiterals()) {
536 for (
auto& constr : subConstraints) {
541 node = aggr->getTargetExpression() !=
nullptr
542 ? mk<Aggregator>(aggr->getBaseOperator(),
544 std::move(newBodyLiterals))
545 :
mk<
Aggregator>(aggr->getBaseOperator(), nullptr,
std::move(newBodyLiterals));
552 if (
auto* arg =
dynamic_cast<Argument*
>(node.get())) {
553 if (!isA<ast::Variable>(arg)) {
554 std::stringstream name;
555 name <<
"@abdul" << changeCount++;
558 if (isA<UnnamedVariable>(arg)) {
559 return mk<ast::Variable>(name.str());
563 constraints.insert(mk<BinaryConstraint>(
565 return mk<ast::Variable>(name.str());
575 bool changed =
false;
576 for (
auto* clause : program.getClauses()) {
578 std::set<Own<BinaryConstraint>> constraintsToAdd;
579 argument_normaliser update(constraintsToAdd, changeCount);
582 clause->getHead()->apply(update);
585 for (Literal* lit : clause->getBodyLiterals()) {
586 if (
auto* bc =
dynamic_cast<BinaryConstraint*
>(lit)) {
587 if (
isEqConstraint(bc->getBaseOperator()) && isA<ast::Variable>(bc->getLHS())) {
596 for (Argument* arg : rec.getArguments()) {
602 for (
auto& constraint : constraintsToAdd) {
606 changed |= changeCount != 0;
612 QualifiedName AdornDatabaseTransformer::getAdornmentID(
613 const QualifiedName& relName,
const std::string& adornmentMarker) {
614 if (adornmentMarker ==
"")
return relName;
615 QualifiedName adornmentID(relName);
616 std::stringstream adornmentMarkerRepr;
617 adornmentMarkerRepr <<
"{" << adornmentMarker <<
"}";
618 adornmentID.append(adornmentMarkerRepr.str());
622 Own<Clause> AdornDatabaseTransformer::adornClause(
const Clause* clause,
const std::string& adornmentMarker) {
644 for (
size_t i = 0;
i < adornmentMarker.length();
i++) {
646 assert(var !=
nullptr &&
"expected only variables in head");
647 if (adornmentMarker[
i] ==
'b') {
653 auto adornedClause = mk<Clause>();
658 "clauses with plans should be ignored");
663 auto adornedHeadAtom = mk<Atom>(getAdornmentID(relName, adornmentMarker));
664 assert((adornmentMarker ==
"" || headArgs.size() == adornmentMarker.length()) &&
665 "adornment marker should correspond to head atom variables");
666 for (
const auto* arg : headArgs) {
668 assert(var !=
nullptr &&
"expected only variables in head");
671 adornedClause->setHead(std::move(adornedHeadAtom));
674 std::vector<Own<Literal>> adornedBodyLiterals;
676 if (
const auto* negation =
dynamic_cast<const Negation*
>(lit)) {
678 const auto negatedAtomName = negation->getAtom()->getQualifiedName();
679 assert(
contains(weaklyIgnoredRelations, negatedAtomName) &&
680 "negated atoms should not be adorned");
681 queueAdornment(negatedAtomName,
"");
684 if (!isA<Atom>(lit)) {
690 const auto* atom =
dynamic_cast<const Atom*
>(lit);
691 assert(atom !=
nullptr &&
"expected atom");
694 std::stringstream atomAdornment;
695 if (!
contains(weaklyIgnoredRelations, atom->getQualifiedName())) {
696 for (
const auto* arg : atom->getArguments()) {
697 const auto* var =
dynamic_cast<const ast::Variable*
>(arg);
698 assert(var !=
nullptr &&
"expected only variables in atom");
699 atomAdornment << (variableBindings.
isBound(var->getName()) ?
"b" :
"f");
702 auto currAtomAdornmentID = getAdornmentID(atom->getQualifiedName(), atomAdornment.str());
705 queueAdornment(atom->getQualifiedName(), atomAdornment.str());
709 adornedBodyAtom->setQualifiedName(currAtomAdornmentID);
710 adornedBodyLiterals.push_back(std::move(adornedBodyAtom));
713 for (
const auto* arg : atom->getArguments()) {
714 const auto* var =
dynamic_cast<const ast::Variable*
>(arg);
715 assert(var !=
nullptr &&
"expected only variables in atom");
719 adornedClause->setBodyLiterals(std::move(adornedBodyLiterals));
721 return adornedClause;
724 bool AdornDatabaseTransformer::transform(TranslationUnit& translationUnit) {
725 Program& program = translationUnit.getProgram();
726 const auto& ioTypes = *translationUnit.getAnalysis<analysis::IOTypeAnalysis>();
727 weaklyIgnoredRelations = getWeaklyIgnoredRelations(translationUnit);
730 for (
const auto*
rel : program.getRelations()) {
731 if (ioTypes.isOutput(
rel) || ioTypes.isPrintSize(
rel)) {
732 queueAdornment(
rel->getQualifiedName(),
"");
737 while (hasAdornmentToProcess()) {
739 const auto& [relName, adornmentMarker] = nextAdornmentToProcess();
742 if (adornmentMarker !=
"") {
744 assert(
rel !=
nullptr &&
"relation does not exist");
746 auto adornedRelation = mk<Relation>(getAdornmentID(relName, adornmentMarker));
747 for (
const auto* attr :
rel->getAttributes()) {
750 program.addRelation(std::move(adornedRelation));
754 for (
const auto* clause :
getClauses(program, relName)) {
755 if (adornmentMarker ==
"") {
758 auto adornedClause = adornClause(clause, adornmentMarker);
759 adornedClauses.push_back(std::move(adornedClause));
764 for (
const auto& clause : redundantClauses) {
765 program.removeClause(clause.get());
768 for (
auto& clause : adornedClauses) {
772 return !adornedClauses.empty() || !redundantClauses.empty();
775 QualifiedName NegativeLabellingTransformer::getNegativeLabel(
const QualifiedName& name) {
776 QualifiedName newName(name);
777 newName.prepend(
"@neglabel");
782 std::stringstream label;
783 label <<
"@poscopy_" <<
count;
785 labelledName.
prepend(label.str());
789 bool LabelDatabaseTransformer::isNegativelyLabelled(
const QualifiedName& name) {
791 assert(!qualifiers.empty() &&
"unexpected empty qualifier list");
792 return qualifiers[0] ==
"@neglabel";
799 std::set<QualifiedName> relationsToLabel;
800 std::set<Own<Clause>> clausesToAdd;
801 const auto& relationsToNotLabel = getRelationsToNotLabel(translationUnit);
809 if (
contains(relationsToNotLabel, relName))
return;
810 atom->setQualifiedName(getNegativeLabel(relName));
811 relationsToLabel.insert(relName);
816 if (
contains(relationsToNotLabel, relName))
return;
817 const_cast<Atom&
>(atom).setQualifiedName(getNegativeLabel(relName));
818 relationsToLabel.insert(relName);
823 for (
size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
825 const auto& stratumRels = sccGraph.getInternalRelations(stratum);
826 std::map<QualifiedName, QualifiedName> newSccFriendNames;
827 for (
const auto*
rel : stratumRels) {
828 auto relName =
rel->getQualifiedName();
829 if (
contains(relationsToNotLabel, relName))
continue;
830 relationsToLabel.insert(relName);
831 newSccFriendNames[relName] = getNegativeLabel(relName);
835 for (
const auto*
rel : stratumRels) {
836 if (
contains(relationsToNotLabel,
rel->getQualifiedName()))
continue;
837 for (
auto* clause :
getClauses(program,
rel->getQualifiedName())) {
840 clausesToAdd.insert(std::move(neggedClause));
846 for (
const auto& relName : relationsToLabel) {
847 const auto* originalRel =
getRelation(program, relName);
848 assert(originalRel !=
nullptr &&
"unlabelled relation does not exist");
850 labelledRelation->setQualifiedName(getNegativeLabel(relName));
851 program.addRelation(std::move(labelledRelation));
855 for (
const auto& clause : clausesToAdd) {
859 return !relationsToLabel.empty();
862 bool PositiveLabellingTransformer::transform(TranslationUnit& translationUnit) {
863 Program& program = translationUnit.getProgram();
864 const auto& sccGraph = *translationUnit.getAnalysis<analysis::SCCGraphAnalysis>();
865 const auto& precedenceGraph = translationUnit.getAnalysis<analysis::PrecedenceGraphAnalysis>()->graph();
866 const auto& relationsToNotLabel = getRelationsToNotLabel(translationUnit);
869 std::set<size_t> neglabelledStrata;
870 std::map<size_t, size_t> originalStrataCopyCount;
871 for (
size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
872 size_t numNeggedRelations = 0;
873 const auto& stratumRels = sccGraph.getInternalRelations(stratum);
876 for (
const auto*
rel : stratumRels) {
877 if (isNegativelyLabelled(
rel->getQualifiedName())) {
878 numNeggedRelations++;
881 assert((numNeggedRelations == 0 || numNeggedRelations == stratumRels.size()) &&
882 "stratum cannot contain a mix of neglabelled and unlabelled relations");
884 if (numNeggedRelations > 0) {
886 neglabelledStrata.insert(stratum);
889 originalStrataCopyCount[stratum] = 0;
895 std::map<size_t, std::set<size_t>> dependentStrata;
896 for (
size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
897 dependentStrata[stratum] = std::set<size_t>();
899 for (
const auto*
rel : program.getRelations()) {
900 size_t stratum = sccGraph.getSCC(
rel);
901 precedenceGraph.visitDepthFirst(
rel, [&](
const auto* dependentRel) {
902 dependentStrata[stratum].insert(sccGraph.getSCC(dependentRel));
908 for (
size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) {
909 if (!
contains(neglabelledStrata, stratum))
continue;
912 for (
const auto*
rel : sccGraph.getInternalRelations(stratum)) {
913 assert(isNegativelyLabelled(
rel->getQualifiedName()) &&
914 "should only be looking at neglabelled strata");
916 std::set<QualifiedName> relsToCopy;
919 for (
const auto* clause :
clauses) {
922 if (!
contains(relationsToNotLabel, name) && !isNegativelyLabelled(name)) {
923 relsToCopy.insert(name);
930 std::map<QualifiedName, QualifiedName> labelledNames;
931 for (
const auto& relName : relsToCopy) {
932 size_t relStratum = sccGraph.getSCC(
getRelation(program, relName));
933 size_t copyCount = originalStrataCopyCount.at(relStratum) + 1;
934 labelledNames[relName] = getPositiveLabel(relName, copyCount);
941 for (
int preStratum = stratum - 1; preStratum >= 0; preStratum--) {
942 if (
contains(neglabelledStrata, preStratum))
continue;
943 if (!
contains(dependentStrata[preStratum], stratum))
continue;
945 for (
const auto*
rel : sccGraph.getInternalRelations(preStratum)) {
946 if (
contains(relationsToNotLabel,
rel->getQualifiedName()))
continue;
948 for (
const auto* clause :
getClauses(program,
rel->getQualifiedName())) {
950 std::map<QualifiedName, QualifiedName> labelledNames;
952 const auto& relName = atom.getQualifiedName();
953 if (
contains(relationsToNotLabel, relName) || isNegativelyLabelled(relName))
return;
954 size_t relStratum = sccGraph.getSCC(
getRelation(program, relName));
955 size_t copyCount = originalStrataCopyCount.at(relStratum) + 1;
956 labelledNames[relName] = getPositiveLabel(relName, copyCount);
962 program.addClause(std::move(labelledClause));
966 originalStrataCopyCount[preStratum]++;
971 bool changed =
false;
972 for (
const auto& [stratum, numCopies] : originalStrataCopyCount) {
973 const auto& stratumRels = sccGraph.getInternalRelations(stratum);
975 for (
auto*
rel : stratumRels) {
977 newRelation->setQualifiedName(getPositiveLabel(newRelation->getQualifiedName(),
copy + 1));
978 program.addRelation(std::move(newRelation));
986 bool MagicSetCoreTransformer::isAdorned(
const QualifiedName& name) {
988 auto qualifiers = name.getQualifiers();
989 assert(!qualifiers.empty() &&
"unexpected empty qualifier list");
990 auto finalQualifier = qualifiers[qualifiers.size() - 1];
991 assert(finalQualifier.length() > 0 &&
"unexpected empty qualifier");
994 if (finalQualifier[0] ==
'{' && finalQualifier[finalQualifier.length() - 1] ==
'}') {
995 for (
size_t i = 1;
i < finalQualifier.length() - 1;
i++) {
996 char curBindingType = finalQualifier[
i];
997 if (curBindingType !=
'b' && curBindingType !=
'f') {
1006 std::string MagicSetCoreTransformer::getAdornment(
const QualifiedName& name) {
1007 assert(isAdorned(name) &&
"relation not adorned");
1009 auto finalQualifier = qualifiers[qualifiers.size() - 1];
1010 std::stringstream binding;
1011 for (
size_t i = 1;
i < finalQualifier.length() - 1;
i++) {
1012 binding << finalQualifier[
i];
1014 return binding.str();
1018 assert(isAdorned(name) &&
"cannot magify unadorned predicates");
1020 magicRelName.
prepend(
"@magic");
1021 return magicRelName;
1024 Own<Atom> MagicSetCoreTransformer::createMagicAtom(
const Atom* atom) {
1028 auto magicAtom = mk<Atom>(getMagicName(origRelName));
1030 auto adornmentMarker = getAdornment(origRelName);
1031 for (
size_t i = 0;
i < args.size();
i++) {
1032 if (adornmentMarker[
i] ==
'b') {
1040 void MagicSetCoreTransformer::addRelevantVariables(
1041 std::set<std::string>& variables,
const std::vector<const BinaryConstraint*> eqConstraints) {
1043 auto isFullyBound = [&](
const Argument* arg) {
1044 bool fullyBound =
true;
1053 if (lhsVar ==
nullptr)
return true;
1056 if (!
contains(variables, lhsVar->getName())) {
1057 if (isFullyBound(
rhs)) {
1058 variables.insert(lhsVar->getName());
1066 bool fixpointReached =
true;
1067 if (
const auto* rhsRec =
dynamic_cast<const RecordInit*
>(
rhs)) {
1068 for (
const auto* arg : rhsRec->getArguments()) {
1069 const auto* subVar =
dynamic_cast<const ast::Variable*
>(arg);
1070 assert(subVar !=
nullptr &&
"expected only variable arguments");
1071 if (!
contains(variables, subVar->getName())) {
1072 fixpointReached =
false;
1073 variables.insert(subVar->getName());
1078 return fixpointReached;
1082 bool fixpointReached =
false;
1083 while (!fixpointReached) {
1084 fixpointReached =
true;
1085 for (
const auto* eqConstraint : eqConstraints) {
1086 assert(
isEqConstraint(eqConstraint->getBaseOperator()) &&
"expected only eq constraints");
1087 fixpointReached &= addLocallyRelevantVariables(eqConstraint->getLHS(), eqConstraint->getRHS());
1088 fixpointReached &= addLocallyRelevantVariables(eqConstraint->getRHS(), eqConstraint->getLHS());
1093 Own<Clause> MagicSetCoreTransformer::createMagicClause(
const Atom* atom,
1094 const std::vector<Own<Atom>>& constrainingAtoms,
1095 const std::vector<const BinaryConstraint*> eqConstraints) {
1096 auto magicHead = createMagicAtom(atom);
1097 auto magicClause = mk<Clause>();
1100 for (
const auto& bindingAtom : constrainingAtoms) {
1105 std::set<std::string> relevantVariables;
1107 constrainingAtoms, [&](
const ast::Variable& var) { relevantVariables.insert(var.getName()); });
1109 addRelevantVariables(relevantVariables, eqConstraints);
1112 for (
const auto* eqConstraint : eqConstraints) {
1113 bool addConstraint =
true;
1115 if (!
contains(relevantVariables, var.getName())) {
1116 addConstraint = false;
1120 if (addConstraint) magicClause->addToBody(
souffle::clone(eqConstraint));
1123 magicClause->setHead(std::move(magicHead));
1127 std::vector<const BinaryConstraint*> MagicSetCoreTransformer::getBindingEqualityConstraints(
1128 const Clause* clause) {
1129 std::vector<const BinaryConstraint*> equalityConstraints;
1130 for (
const auto* lit : clause->getBodyLiterals()) {
1131 const auto* bc =
dynamic_cast<const BinaryConstraint*
>(lit);
1132 if (bc ==
nullptr || !
isEqConstraint(bc->getBaseOperator()))
continue;
1133 if (isA<ast::Variable>(bc->getLHS()) || isA<Constant>(bc->getRHS())) {
1134 bool containsAggrs =
false;
1136 if (!containsAggrs) {
1137 equalityConstraints.push_back(bc);
1141 return equalityConstraints;
1144 bool MagicSetCoreTransformer::transform(
TranslationUnit& translationUnit) {
1146 std::set<Own<Clause>> clausesToRemove;
1147 std::set<Own<Clause>> clausesToAdd;
1153 const auto* head = clause->getHead();
1154 auto relName = head->getQualifiedName();
1157 if (!isAdorned(relName)) {
1162 auto magicAtom = createMagicAtom(head);
1163 auto refinedClause = mk<Clause>();
1166 for (
auto* literal : clause->getBodyLiterals()) {
1169 clausesToAdd.insert(std::move(refinedClause));
1173 std::vector<const BinaryConstraint*> eqConstraints = getBindingEqualityConstraints(clause);
1174 std::vector<Own<Atom>> atomsToTheLeft;
1175 if (isAdorned(relName)) {
1178 atomsToTheLeft.push_back(createMagicAtom(clause->getHead()));
1180 for (
const auto* lit : clause->getBodyLiterals()) {
1181 const auto* atom =
dynamic_cast<const Atom*
>(lit);
1182 if (atom ==
nullptr)
continue;
1183 if (!isAdorned(atom->getQualifiedName())) {
1189 auto magicClause = createMagicClause(atom, atomsToTheLeft, eqConstraints);
1191 clausesToAdd.insert(std::move(magicClause));
1195 for (
auto& clause : clausesToAdd) {
1198 for (
const auto& clause : clausesToRemove) {
1203 bool changed =
false;
1205 const auto& origName =
rel->getQualifiedName();
1206 if (!isAdorned(origName))
continue;
1207 auto magicRelation = mk<Relation>(getMagicName(origName));
1209 auto adornmentMarker = getAdornment(origName);
1210 for (
size_t i = 0;
i < attributes.size();
i++) {
1211 if (adornmentMarker[
i] ==
'b') {