#include "PartialRedundancyHandler.hpp"
#include "Kernel/Clause.hpp"
#include "Kernel/EqHelper.hpp"
#include "Kernel/SortHelper.hpp"
#include "Kernel/SubstHelper.hpp"
#include "Indexing/CodeTreeInterfaces.hpp"
#include "Indexing/ResultSubstitution.hpp"
#include "Statistics.hpp"
using namespace std;
using namespace Indexing;
namespace Shell
{
template<class T>
bool checkVars(const TermStack& ts, T s)
{
DHSet<TermList> vars;
for (const auto& t : ts) {
VariableIterator vit(t);
while (vit.hasNext()) {
vars.insert(vit.next());
}
}
VariableIterator vit(s);
while (vit.hasNext()) {
auto var = vit.next();
if (!vars.contains(var)) {
return false;
}
}
return true;
}
class PartialRedundancyHandler::ConstraintIndex
: public CodeTree
{
public:
ConstraintIndex(Clause* cl) : _varSorts()
{
_clauseCodeTree=false;
_onCodeOpDestroying = onCodeOpDestroying;
#if VDEBUG
_cl = cl;
#endif
for (unsigned i = 0; i < cl->length(); i++) {
SortHelper::collectVariableSorts((*cl)[i], _varSorts);
}
}
bool check(const Ordering* ord, ResultSubstitution* subst, bool result, LiteralSet& lits, const SplitSet* splits)
{
if (_varSorts.isEmpty()) {
return true;
}
auto ts = getInstances([subst,result](unsigned v) { return subst->applyTo(TermList::var(v),result); });
return !check(ts, ord, lits, splits);
}
void insert(const Ordering* ord, ResultSubstitution* subst, bool result, Splitter* splitter,
OrderingConstraints&& ordCons, LiteralSet&& lits, SplitSet* splits)
{
auto ts = getInstances([subst,result](unsigned v) { return subst->applyTo(TermList::var(v),result); });
insert(ord, ts, createEntry(ts, splitter, std::move(ordCons), std::move(lits), splits));
}
private:
#if VDEBUG
Clause* _cl;
#endif
void insert(const Ordering* ord, const TermStack& ts, PartialRedundancyEntry* ptr)
{
if (!isEmpty()) {
VariantMatcher vm;
Stack<CodeOp*> firstsInBlocks;
FlatTerm* ft = FlatTerm::createUnexpanded(ts);
vm.init(ft, this, &firstsInBlocks);
if (vm.next()) {
ASS(vm.op->isSuccess());
auto container = vm.op->template getSuccessResult<EntryContainer>();
container->tod->insert(ptr->ordCons, ptr);
container->entries.push(ptr);
ft->destroy();
return;
}
ft->destroy();
}
CodeStack code;
TermCompiler compiler(code);
for (const auto& t : ts) {
if (t.isVar()) {
compiler.handleVar(t.var());
continue;
}
ASS(t.isTerm());
compiler.handleTerm(t.term());
}
compiler.updateCodeTree(this);
auto es = new EntryContainer();
es->tod = ord->createTermOrderingDiagram();
es->tod->insert(ptr->ordCons, ptr);
es->entries.push(ptr);
code.push(CodeOp::getSuccess(es));
incorporate(code);
}
bool check(const TermStack& ts, const Ordering* ord, const LiteralSet& lits, const SplitSet* splits)
{
if (isEmpty()) {
return false;
}
static SubstMatcher matcher;
struct Applicator : public SubstApplicator {
TermList operator()(unsigned v) const override { return matcher.bindings[v]; }
} applicator;
matcher.init(this, ts);
EntryContainer* ec;
while ((ec = matcher.next()))
{
ASS(ec->tod);
ec->tod->init(&applicator);
PartialRedundancyEntry* e;
while ((e = static_cast<PartialRedundancyEntry*>(ec->tod->next()))) {
if (!e->active) {
continue;
}
if (!e->splits->isSubsetOf(splits)) {
continue;
}
auto subsetof = e->lits.iter().all([&lits,&applicator](Literal* lit) {
return lits.contains(SubstHelper::apply(lit,applicator));
});
if (!subsetof) {
continue;
}
if (e->ordCons.isNonEmpty()) {
env.statistics->inferencesSkippedDueToOrderingConstraints++;
}
if (e->lits.size()>0) {
env.statistics->inferencesSkippedDueToLiteralConstraints++;
}
if (!e->splits->isEmpty()) {
env.statistics->inferencesSkippedDueToAvatarConstraints++;
}
matcher.reset();
return true;
}
}
matcher.reset();
return false;
}
template<class Applicator>
TermStack getInstances(Applicator applicator) const
{
DHMap<unsigned,TermList>::Iterator vit(_varSorts);
TermStack res;
while (vit.hasNext()) {
auto v = vit.nextKey();
res.push(applicator(v));
}
return res;
}
DHMap<unsigned,TermList> _varSorts;
PartialRedundancyEntry* createEntry(const TermStack& ts, Splitter* splitter, OrderingConstraints&& ordCons, LiteralSet&& lits, SplitSet* splits) const
{
auto e = new PartialRedundancyEntry();
Renaming r;
if (ordCons.isNonEmpty() || lits.size()>0) {
for (const auto t : ts) {
r.normalizeVariables(t);
}
}
for (auto& ordCon : ordCons) {
ASS(checkVars(ts,ordCon.lhs));
ASS(checkVars(ts,ordCon.rhs));
ASS(ordCon.lhs.containsAllVariablesOf(ordCon.rhs));
ordCon.lhs = r.apply(ordCon.lhs);
ordCon.rhs = r.apply(ordCon.rhs);
}
e->ordCons = std::move(ordCons);
#if VDEBUG
lits.iter().forEach([&ts](Literal* lit) {
ASS(checkVars(ts,lit));
});
#endif
e->lits.insertFromIterator(lits.iter().map([&r](Literal* lit) {
return r.apply(lit);
}));
ASS(splits);
e->splits = splits;
if (!splits->isEmpty()) {
splitter->addPartialRedundancyEntry(splits, e);
}
return e;
}
struct SubstMatcher
: public Matcher
{
void init(CodeTree* tree, const TermStack& ts)
{
Matcher::init(tree,tree->getEntryPoint());
ft = FlatTerm::createUnexpanded(ts);
op=entry;
tp=0;
}
void reset()
{
ft->destroy();
}
EntryContainer* next()
{
if (finished()) {
return nullptr;
}
_matched=execute();
if (!_matched) {
return nullptr;
}
ASS(op->isSuccess());
return op->getSuccessResult<EntryContainer>();
}
};
struct VariantMatcher
: public RemovingMatcher<true>
{
public:
void init(FlatTerm* ft_, CodeTree* tree_, Stack<CodeOp*>* firstsInBlocks_) {
RemovingMatcher::init(tree_->getEntryPoint(), 0, 0, tree_, firstsInBlocks_);
ft=ft_;
tp=0;
op=entry;
}
};
static void onCodeOpDestroying(CodeOp* op) {
if (op->isSuccess()) {
auto es = op->getSuccessResult<EntryContainer>();
iterTraits(decltype(es->entries)::Iterator(es->entries))
.forEach([](auto e) {
e->release();
});
delete es;
}
}
};
PartialRedundancyHandler* PartialRedundancyHandler::create(const Options& opts, const Ordering* ord, Splitter* splitter)
{
if (!opts.partialRedundancyCheck()) {
return new PartialRedundancyHandlerImpl<false,false,false,false>(opts,ord,splitter);
}
auto ordC = opts.partialRedundancyOrderingConstraints();
auto avatarC = opts.splitting() && opts.partialRedundancyAvatarConstraints();
auto litC = opts.partialRedundancyLiteralConstraints();
if (ordC) {
if (avatarC) {
if (litC) {
return new PartialRedundancyHandlerImpl<true,true,true,true>(opts,ord,splitter);
}
return new PartialRedundancyHandlerImpl<true,true,true,false>(opts,ord,splitter);
}
if (litC) {
return new PartialRedundancyHandlerImpl<true,true,false,true>(opts,ord,splitter);
}
return new PartialRedundancyHandlerImpl<true,true,false,false>(opts,ord,splitter);
}
if (avatarC) {
if (litC) {
return new PartialRedundancyHandlerImpl<true,false,true,true>(opts,ord,splitter);
}
return new PartialRedundancyHandlerImpl<true,false,true,false>(opts,ord,splitter);
}
if (litC) {
return new PartialRedundancyHandlerImpl<true,false,false,true>(opts,ord,splitter);
}
return new PartialRedundancyHandlerImpl<true,false,false,false>(opts,ord,splitter);
}
void PartialRedundancyHandler::destroyClauseData(Clause* cl)
{
ConstraintIndex* ptr = nullptr;
clauseData.pop(cl, ptr);
delete ptr;
}
PartialRedundancyHandler::ConstraintIndex** PartialRedundancyHandler::getDataPtr(Clause* cl, bool doAllocate)
{
if (!doAllocate) {
return clauseData.findPtr(cl);
}
ConstraintIndex** ptr;
clauseData.getValuePtr(cl, ptr, nullptr);
if (!*ptr) {
*ptr = new ConstraintIndex(cl);
}
return ptr;
}
DHMap<Clause*,typename PartialRedundancyHandler::ConstraintIndex*> PartialRedundancyHandler::clauseData;
template<bool enabled, bool ordC, bool avatarC, bool litC>
bool PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::checkSuperposition(
Clause* eqClause, Literal* eqLit, Clause* rwClause, Literal* rwLit,
bool eqIsResult, ResultSubstitution* subs) const
{
if constexpr (!enabled) {
return true;
}
auto rwLits = getRemainingLiterals(rwClause, rwLit, subs, !eqIsResult);
auto rwSplits = getRemainingSplits(rwClause, eqClause);
auto eqClDataPtr = getDataPtr(eqClause, false);
if (eqClDataPtr && !(*eqClDataPtr)->check(_ord, subs, eqIsResult, rwLits, rwSplits)) {
env.statistics->skippedSuperposition++;
return false;
}
auto eqLits = getRemainingLiterals(eqClause, eqLit, subs, eqIsResult);
auto eqSplits = getRemainingSplits(eqClause, rwClause);
auto rwClDataPtr = getDataPtr(rwClause, false);
if (rwClDataPtr && !(*rwClDataPtr)->check(_ord, subs, !eqIsResult, eqLits, eqSplits)) {
env.statistics->skippedSuperposition++;
return false;
}
return true;
}
bool checkOrConstrainGreater(Ordering::Result value, TermList lhs, TermList rhs, OrderingConstraints& cons)
{
switch (value) {
case Ordering::GREATER:
break;
case Ordering::EQUAL:
case Ordering::LESS:
return false;
case Ordering::INCOMPARABLE: {
if (!lhs.containsAllVariablesOf(rhs)) {
return false;
}
cons.push({ lhs, rhs, Ordering::GREATER });
break;
}
}
return true;
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
void PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::insertSuperposition(
Clause* eqClause, Clause* rwClause, TermList rwTerm, TermList rwTermS, TermList tgtTermS, TermList eqLHS,
Literal* rwLitS, Literal* eqLit, Ordering::Result eqComp, bool eqIsResult, ResultSubstitution* subs) const
{
if constexpr (!enabled) {
return;
}
OrderingConstraints ordCons;
if (!checkOrConstrainGreater(Ordering::reverse(eqComp), rwTermS, tgtTermS, ordCons)) {
return;
}
if (!compareWithSuperpositionPremise(rwClause, rwLitS, rwTerm, rwTermS, tgtTermS, eqClause, eqLHS, ordCons)) {
return;
}
if constexpr (!ordC) {
if (ordCons.isNonEmpty()) {
return;
}
}
auto lits = getRemainingLiterals(eqClause, eqLit, subs, eqIsResult);
auto splits = getRemainingSplits(eqClause, rwClause);
tryInsert(rwClause, subs, !eqIsResult, eqClause, std::move(ordCons), std::move(lits), splits);
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
bool PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::handleResolution(
Clause* queryCl, Literal* queryLit, Clause* resultCl, Literal* resultLit, ResultSubstitution* subs) const
{
if constexpr (!enabled) {
return true;
}
auto resultLits = getRemainingLiterals(resultCl, resultLit, subs, true);
auto resultSplits = getRemainingSplits(resultCl, queryCl);
auto dataPtr = getDataPtr(queryCl, false);
if (dataPtr && !(*dataPtr)->check(_ord, subs, false, resultLits, resultSplits)) {
env.statistics->skippedResolution++;
return false;
}
auto queryLits = getRemainingLiterals(queryCl, queryLit, subs, false);
auto querySplits = getRemainingSplits(queryCl, resultCl);
dataPtr = getDataPtr(resultCl, false);
if (dataPtr && !(*dataPtr)->check(_ord, subs, true, queryLits, querySplits)) {
env.statistics->skippedResolution++;
return false;
}
if (resultLit->isPositive()) {
tryInsert(queryCl, subs, false, resultCl, OrderingConstraints(), std::move(resultLits), resultSplits);
} else {
ASS(queryLit->isPositive());
tryInsert(resultCl, subs, true, queryCl, OrderingConstraints(), std::move(queryLits), querySplits);
}
return true;
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
bool PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::compareWithSuperpositionPremise(
Clause* rwCl, Literal* rwLitS, TermList rwTerm, TermList rwTermS, TermList tgtTermS, Clause* eqCl, TermList eqLHS, OrderingConstraints& cons) const
{
if (!_redundancyCheck) {
return true;
}
if (!rwLitS->isEquality() || (rwTermS!=*rwLitS->nthArgument(0) && rwTermS!=*rwLitS->nthArgument(1))) {
return true;
}
auto other = EqHelper::getOtherEqualitySide(rwLitS, rwTermS);
const bool canEqEncompass = (eqCl->length() == 1);
const bool canRwEncompass = (rwLitS->isPositive() && rwCl->length() == 1);
if (_encompassing) {
if (canEqEncompass) {
if (!canRwEncompass) {
return true;
}
if (MatchingUtils::matchTerms(eqLHS, rwTerm)) {
if (!MatchingUtils::matchTerms(rwTerm, eqLHS)) {
return true;
}
return checkOrConstrainGreater(_ord->compare(other, tgtTermS), other, tgtTermS, cons);
}
if (!MatchingUtils::matchTerms(rwTerm, eqLHS)) {
return checkOrConstrainGreater(_ord->compare(rwTermS, other), rwTermS, other, cons)
&& checkOrConstrainGreater(_ord->compare(other, tgtTermS), other, tgtTermS, cons);
}
return false;
}
if (canRwEncompass) {
return false;
}
}
return checkOrConstrainGreater(_ord->compare(other, tgtTermS), other, tgtTermS, cons);
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
LiteralSet PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::getRemainingLiterals(
Clause* cl, Literal* lit, ResultSubstitution* subs, bool result) const
{
LiteralSet res;
if constexpr (litC) {
res.insertFromIterator(cl->iterLits().filter([lit](Literal* other) {
return other != lit && lit->containsAllVariablesOf(other);
}).map([subs,result](Literal* other) {
return subs->applyTo(other, result);
}));
}
return res;
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
const SplitSet* PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::getRemainingSplits(Clause* cl, Clause* other) const
{
if constexpr (!avatarC) {
return SplitSet::getEmpty();
}
return cl->splits()->subtract(other->splits());
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
void PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::tryInsert(
Clause* into, ResultSubstitution* subs, bool result, Clause* cl, OrderingConstraints&& ordCons, LiteralSet&& lits, SplitSet* splits) const
{
if constexpr (!enabled) {
return;
}
if (cl->numSelected()!=1 || cl->length()>lits.size()+1) {
return;
}
if constexpr (!avatarC) {
if (!cl->noSplits()) {
return;
}
}
auto dataPtr = getDataPtr(into, true);
(*dataPtr)->insert(_ord, subs, result, _splitter, std::move(ordCons), std::move(lits), splits);
}
template<bool enabled, bool ordC, bool avatarC, bool litC>
void PartialRedundancyHandlerImpl<enabled, ordC, avatarC, litC>::checkEquations(Clause* cl) const
{
if (!enabled || !ordC) {
return;
}
cl->iterLits().forEach([cl,this](Literal* lit){
if (!lit->isEquality() || lit->isNegative()) {
return;
}
auto t0 = lit->termArg(0);
auto t1 = lit->termArg(1);
RobSubstitution subs;
if (!subs.unify(t0,0,t1,0)) {
return;
}
auto clDataPtr = getDataPtr(cl, true);
auto rsubs = ResultSubstitution::fromSubstitution(&subs, 0, 0);
(*clDataPtr)->insert(_ord, rsubs.ptr(), false, nullptr, OrderingConstraints(), LiteralSet(), SplitSet::getEmpty());
});
}
}