#include #include #include #include #include #include #include #include namespace anthem { //////////////////////////////////////////////////////////////////////////////////////////////////// // // Completion // //////////////////////////////////////////////////////////////////////////////////////////////////// // Builds the conjunction within the completed formula for a given predicate ast::Formula buildCompletedFormulaDisjunction(const ast::Predicate &predicate, const ast::VariableDeclarationPointers ¶meters, std::vector &scopedFormulas) { auto disjunction = ast::Formula::make(); assert(predicate.arguments.size() == parameters.size()); // Build the disjunction of all formulas with the predicate as consequent for (auto &scopedFormula : scopedFormulas) { assert(scopedFormula.formula.is()); auto &implies = scopedFormula.formula.get(); if (!implies.consequent.is()) continue; auto &otherPredicate = implies.consequent.get(); if (predicate.declaration != otherPredicate.declaration) continue; assert(otherPredicate.arguments.size() == parameters.size()); auto &freeVariables = scopedFormula.freeVariables; // Each formula with the predicate as its consequent currently has its own copy of the predicate’s parameters // These need to be linked to the new, unique set of parameters // First, remove the free variables whose occurrences will be relinked, which is why they are no longer needed const auto isFreeVariableUnneeded = [&](const auto &freeVariable) { const auto matchesVariableToBeReplaced = std::find_if(otherPredicate.arguments.cbegin(), otherPredicate.arguments.cend(), [&](const ast::Term &argument) { assert(argument.is()); const auto &otherVariable = argument.get(); return (freeVariable.get() == otherVariable.declaration); }); return (matchesVariableToBeReplaced != otherPredicate.arguments.cend()); }; freeVariables.erase(std::remove_if(freeVariables.begin(), freeVariables.end(), isFreeVariableUnneeded), freeVariables.end()); // Currently, only rules with singleton heads are supported // Rules with multiple elements in the head are not yet handled correctly by the head variable detection mechanism for (const auto &freeVariable : freeVariables) if (freeVariable->type == ast::VariableDeclaration::Type::Head) throw CompletionException("cannot perform completion, only singleton rule heads supported currently"); // Second, link all occurrences of the deleted free variable to the new, unique parameter for (size_t i = 0; i < parameters.size(); i++) { assert(otherPredicate.arguments[i].is()); const auto &otherVariable = otherPredicate.arguments[i].get(); scopedFormula.formula.accept(ast::ReplaceVariableInFormulaVisitor(), scopedFormula.formula, otherVariable.declaration, parameters[i].get()); } if (freeVariables.empty()) disjunction.get().arguments.emplace_back(std::move(implies.antecedent)); else { auto exists = ast::Formula::make(std::move(freeVariables), std::move(implies.antecedent)); disjunction.get().arguments.emplace_back(std::move(exists)); } } return disjunction; } //////////////////////////////////////////////////////////////////////////////////////////////////// // Builds the quantified inner part of the completed formula ast::Formula buildCompletedFormulaQuantified(ast::Predicate &&predicate, ast::Formula &&innerFormula) { assert(innerFormula.is()); if (innerFormula.get().arguments.empty()) return ast::Formula::make(std::move(predicate)); if (innerFormula.get().arguments.size() == 1) innerFormula = std::move(innerFormula.get().arguments.front()); if (innerFormula.is()) { const auto &boolean = innerFormula.get(); if (boolean.value == true) return std::move(predicate); else return ast::Formula::make(std::move(predicate)); } return ast::Formula::make(std::move(predicate), std::move(innerFormula)); } //////////////////////////////////////////////////////////////////////////////////////////////////// ast::Formula completePredicate(ast::PredicateDeclaration &predicateDeclaration, std::vector &scopedFormulas) { // Create new set of parameters for the completed definition for the predicate ast::VariableDeclarationPointers parameters; parameters.reserve(predicateDeclaration.arity()); std::vector arguments; arguments.reserve(predicateDeclaration.arity()); for (size_t i = 0; i < predicateDeclaration.arity(); i++) { parameters.emplace_back(std::make_unique(ast::VariableDeclaration::Type::Head)); arguments.emplace_back(ast::Term::make(parameters.back().get())); } ast::Predicate predicateCopy(&predicateDeclaration, std::move(arguments)); auto completedFormulaDisjunction = buildCompletedFormulaDisjunction(predicateCopy, parameters, scopedFormulas); auto completedFormulaQuantified = buildCompletedFormulaQuantified(std::move(predicateCopy), std::move(completedFormulaDisjunction)); if (parameters.empty()) return completedFormulaQuantified; return ast::Formula::make(std::move(parameters), std::move(completedFormulaQuantified)); } //////////////////////////////////////////////////////////////////////////////////////////////////// ast::Formula completeIntegrityConstraint(ast::ScopedFormula &scopedFormula) { assert(scopedFormula.formula.is()); auto &implies = scopedFormula.formula.get(); assert(implies.consequent.is()); assert(implies.consequent.get().value == false); auto argument = ast::Formula::make(std::move(implies.antecedent)); auto &freeVariables = scopedFormula.freeVariables; if (freeVariables.empty()) return argument; return ast::Formula::make(std::move(freeVariables), std::move(argument)); } //////////////////////////////////////////////////////////////////////////////////////////////////// std::vector complete(std::vector &&scopedFormulas, Context &context) { // Check whether formulas are in normal form for (const auto &scopedFormula : scopedFormulas) { if (!scopedFormula.formula.is()) throw CompletionException("cannot perform completion, formula not in normal form"); auto &implies = scopedFormula.formula.get(); if (!implies.consequent.is() && !implies.consequent.is()) throw CompletionException("cannot perform completion, only single predicates and Booleans supported as formula consequent currently"); } std::sort(context.predicateDeclarations.begin(), context.predicateDeclarations.end(), [](const auto &lhs, const auto &rhs) { const auto order = std::strcmp(lhs->name.c_str(), rhs->name.c_str()); if (order != 0) return (order < 0); return lhs->arity() < rhs->arity(); }); std::vector completedFormulas; // Complete predicates for (auto &predicateDeclaration : context.predicateDeclarations) { if (!predicateDeclaration->isUsed || predicateDeclaration->isExternal) continue; completedFormulas.emplace_back(completePredicate(*predicateDeclaration, scopedFormulas)); } // Complete integrity constraints for (auto &scopedFormula : scopedFormulas) { auto &implies = scopedFormula.formula.get(); if (!implies.consequent.is()) continue; const auto &boolean = implies.consequent.get(); // Rules of the form “F -> #true” are useless if (boolean.value == true) continue; completedFormulas.emplace_back(completeIntegrityConstraint(scopedFormula)); } // Eliminate all predicates that should not be visible in the output eliminateHiddenPredicates(completedFormulas, context); return completedFormulas; } //////////////////////////////////////////////////////////////////////////////////////////////////// }