anthem/src/anthem/Completion.cpp

201 lines
6.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <anthem/Completion.h>
#include <anthem/ASTUtils.h>
#include <anthem/ASTVisitors.h>
namespace anthem
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Completion
//
////////////////////////////////////////////////////////////////////////////////////////////////////
// Checks that two matching predicates (same name, same arity) have the same arguments
void checkMatchingPredicates(const ast::Term &lhs, const ast::Term &rhs)
{
if (!lhs.is<ast::Variable>() || !rhs.is<ast::Variable>())
throw std::runtime_error("cannot preform completion, only variables supported in predicates currently");
if (lhs.get<ast::Variable>().name != rhs.get<ast::Variable>().name)
throw std::runtime_error("cannot perform completion, inconsistent predicate argument naming");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void completePredicate(const ast::Predicate &predicate, std::vector<ast::Formula> &formulas, std::size_t &formulaIndex)
{
// Check that predicate is in normal form
for (auto i = formulaIndex; i < formulas.size(); i++)
{
auto &formula = formulas[i];
assert(formula.is<ast::Implies>());
auto &implies = formula.get<ast::Implies>();
if (!implies.consequent.is<ast::Predicate>())
continue;
auto &otherPredicate = implies.consequent.get<ast::Predicate>();
if (predicate.arity() != otherPredicate.arity() || predicate.name != otherPredicate.name)
continue;
for (std::size_t i = 0; i < predicate.arguments.size(); i++)
checkMatchingPredicates(predicate.arguments[i], otherPredicate.arguments[i]);
}
// Copy the predicates arguments for the completed formula
std::vector<ast::Variable> variables;
variables.reserve(predicate.arguments.size());
for (const auto &argument : predicate.arguments)
{
assert(argument.is<ast::Variable>());
variables.emplace_back(ast::deepCopy(argument.get<ast::Variable>()));
}
auto or_ = ast::Formula::make<ast::Or>();
// Build the conjunction of all formulas with the predicate as consequent
for (auto i = formulaIndex; i < formulas.size();)
{
auto &formula = formulas[i];
assert(formula.is<ast::Implies>());
auto &implies = formula.get<ast::Implies>();
if (!implies.consequent.is<ast::Predicate>())
{
i++;
continue;
}
auto &otherPredicate = implies.consequent.get<ast::Predicate>();
if (predicate.arity() != otherPredicate.arity() || predicate.name != otherPredicate.name)
{
i++;
continue;
}
ast::VariableStack variableStack;
variableStack.push(&variables);
auto variables = ast::collectFreeVariables(implies.antecedent, variableStack);
if (variables.empty())
or_.get<ast::Or>().arguments.emplace_back(std::move(implies.antecedent));
else
{
auto exists = ast::Formula::make<ast::Exists>(std::move(variables), std::move(implies.antecedent));
or_.get<ast::Or>().arguments.emplace_back(std::move(exists));
}
if (i > formulaIndex)
formulas.erase(formulas.begin() + i);
else
i++;
}
auto biconditionalRight = std::move(or_);
// If the disjunction contains only one element, drop the enclosing disjunction
if (biconditionalRight.get<ast::Or>().arguments.size() == 1)
biconditionalRight = biconditionalRight.get<ast::Or>().arguments.front();
// If the biconditional would be of the form “F <-> true” or “F <-> false,” simplify the output
if (biconditionalRight.is<ast::Boolean>())
{
const auto &boolean = biconditionalRight.get<ast::Boolean>();
if (boolean.value == true)
formulas[formulaIndex] = ast::deepCopy(predicate);
else
formulas[formulaIndex] = ast::Formula::make<ast::Not>(ast::deepCopy(predicate));
formulaIndex++;
return;
}
// Build the biconditional within the completed formula
auto biconditional = ast::Formula::make<ast::Biconditional>(ast::deepCopy(predicate), std::move(biconditionalRight));
if (predicate.arguments.empty())
{
formulas[formulaIndex] = std::move(biconditional);
formulaIndex++;
return;
}
auto completedFormula = ast::Formula::make<ast::ForAll>(std::move(variables), std::move(biconditional));
formulas[formulaIndex] = std::move(completedFormula);
formulaIndex++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void completeBoolean(std::vector<ast::Formula> &formulas, std::size_t &formulaIndex)
{
auto &formula = formulas[formulaIndex];
assert(formula.is<ast::Implies>());
auto &implies = formula.get<ast::Implies>();
assert(implies.consequent.is<ast::Boolean>());
auto &boolean = implies.consequent.get<ast::Boolean>();
auto variables = ast::collectFreeVariables(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<ast::Not>(std::move(implies.antecedent));
if (variables.empty())
{
formula = std::move(argument);
formulaIndex++;
return;
}
formula = ast::Formula::make<ast::ForAll>(std::move(variables), std::move(argument));
formulaIndex++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void complete(std::vector<ast::Formula> &formulas)
{
for (const auto &formula : formulas)
{
if (!formula.is<ast::Implies>())
throw std::runtime_error("cannot perform completion, formula not in normal form");
auto &implies = formula.get<ast::Implies>();
if (!implies.consequent.is<ast::Predicate>() && !implies.consequent.is<ast::Boolean>())
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();)
{
auto &formula = formulas[i];
auto &implies = formula.get<ast::Implies>();
if (implies.consequent.is<ast::Predicate>())
{
auto &predicate = implies.consequent.get<ast::Predicate>();
completePredicate(predicate, formulas, i);
}
else if (implies.consequent.is<ast::Boolean>())
completeBoolean(formulas, i);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}