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') {