diff --git a/include/anthem/Body.h b/include/anthem/Body.h index 4f3f347..0f93d1e 100644 --- a/include/anthem/Body.h +++ b/include/anthem/Body.h @@ -102,6 +102,11 @@ struct BodyTermTranslateVisitor struct BodyLiteralTranslateVisitor { + std::experimental::optional visit(const Clingo::AST::Boolean &boolean, const Clingo::AST::Literal &, Context &) + { + return ast::Formula::make(boolean.value); + } + std::experimental::optional visit(const Clingo::AST::Term &term, const Clingo::AST::Literal &literal, Context &context) { return term.data.accept(BodyTermTranslateVisitor(), literal, term, context); diff --git a/src/anthem/Completion.cpp b/src/anthem/Completion.cpp index 5382ddd..4adce41 100644 --- a/src/anthem/Completion.cpp +++ b/src/anthem/Completion.cpp @@ -24,10 +24,10 @@ void checkMatchingPredicates(const ast::Term &lhs, const ast::Term &rhs) //////////////////////////////////////////////////////////////////////////////////////////////////// -void completePredicate(const ast::Predicate &predicate, std::vector &formulas, std::size_t formulasBegin) +void completePredicate(const ast::Predicate &predicate, std::vector &formulas, std::size_t &formulaIndex) { // Check that predicate is in normal form - for (auto i = formulasBegin; i < formulas.size(); i++) + for (auto i = formulaIndex; i < formulas.size(); i++) { auto &formula = formulas[i]; assert(formula.is()); @@ -58,7 +58,7 @@ void completePredicate(const ast::Predicate &predicate, std::vector(); // Build the conjunction of all formulas with the predicate as consequent - for (auto i = formulasBegin; i < formulas.size();) + for (auto i = formulaIndex; i < formulas.size();) { auto &formula = formulas[i]; assert(formula.is()); @@ -91,33 +91,53 @@ void completePredicate(const ast::Predicate &predicate, std::vector().arguments.emplace_back(std::move(exists)); } - if (i > formulasBegin) + if (i > formulaIndex) formulas.erase(formulas.begin() + i); else i++; } - if (or_.get().arguments.size() == 1) - or_ = or_.get().arguments.front(); + auto biconditionalRight = std::move(or_); + + // If the disjunction contains only one element, drop the enclosing disjunction + if (biconditionalRight.get().arguments.size() == 1) + biconditionalRight = biconditionalRight.get().arguments.front(); + + // If the biconditional would be of the form “F <-> true” or “F <-> false,” simplify the output + if (biconditionalRight.is()) + { + const auto &boolean = biconditionalRight.get(); + + if (boolean.value == true) + formulas[formulaIndex] = ast::deepCopy(predicate); + else + formulas[formulaIndex] = ast::Formula::make(ast::deepCopy(predicate)); + + formulaIndex++; + return; + } // Build the biconditional within the completed formula - auto biconditional = ast::Formula::make(ast::deepCopy(predicate), std::move(or_)); + auto biconditional = ast::Formula::make(ast::deepCopy(predicate), std::move(biconditionalRight)); if (predicate.arguments.empty()) { - formulas[formulasBegin] = std::move(biconditional); + formulas[formulaIndex] = std::move(biconditional); + formulaIndex++; return; } auto completedFormula = ast::Formula::make(std::move(variables), std::move(biconditional)); - formulas[formulasBegin] = std::move(completedFormula); + formulas[formulaIndex] = std::move(completedFormula); + formulaIndex++; } //////////////////////////////////////////////////////////////////////////////////////////////////// -void completeBoolean(ast::Formula &formula) +void completeBoolean(std::vector &formulas, std::size_t &formulaIndex) { + auto &formula = formulas[formulaIndex]; assert(formula.is()); auto &implies = formula.get(); assert(implies.consequent.is()); @@ -125,17 +145,24 @@ void completeBoolean(ast::Formula &formula) auto variables = ast::collectFreeVariables(implies.antecedent); - auto argument = (boolean.value == true) - ? std::move(implies.antecedent) - : ast::Formula::make(std::move(implies.antecedent)); + // Implications of the form “T -> true” are useless + if (boolean.value == true) + { + formulas.erase(formulas.begin() + formulaIndex); + return; + } + + auto argument = ast::Formula::make(std::move(implies.antecedent)); if (variables.empty()) { formula = std::move(argument); + formulaIndex++; return; } formula = ast::Formula::make(std::move(variables), std::move(argument)); + formulaIndex++; } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -153,7 +180,7 @@ void complete(std::vector &formulas) throw std::runtime_error("cannot perform completion, only single predicates and Booleans supported as formula consequent currently"); } - for (std::size_t i = 0; i < formulas.size(); i++) + for (std::size_t i = 0; i < formulas.size();) { auto &formula = formulas[i]; auto &implies = formula.get(); @@ -164,7 +191,7 @@ void complete(std::vector &formulas) completePredicate(predicate, formulas, i); } else if (implies.consequent.is()) - completeBoolean(formula); + completeBoolean(formulas, i); } } diff --git a/tests/TestCompletion.cpp b/tests/TestCompletion.cpp index ed2f309..a5cbbfb 100644 --- a/tests/TestCompletion.cpp +++ b/tests/TestCompletion.cpp @@ -69,4 +69,17 @@ TEST_CASE("[completion] Rules are completed", "[completion]") CHECK(output.str() == "q\nr\nt\ns\n"); } + + SECTION("useless implications") + { + input << "#true :- p, q(N), t(1, 2).\n" + "#true.\n" + "h :- #false."; + REQUIRE_NOTHROW(anthem::translate("input", input, context)); + + // TODO: implement completion for unused predicates + CHECK(output.str() == "not h\n"); + } + + // TODO: test collecting free variables }