#include "Kernel/Matcher.hpp"
#include "Lib/Environment.hpp"
#include "Lib/Int.hpp"
#include <algorithm>
#include <type_traits>
#include "SATSubsumption/SATSubsumptionAndResolution.hpp"
#include "SATSubsumptionAndResolution.hpp"
#if PRINT_CLAUSES_SUBS
#include <iostream>
#endif
using namespace Indexing;
using namespace Kernel;
using namespace SATSubsumption;
using namespace std;
const unsigned INVALID = std::numeric_limits<unsigned>::max();
#define PRINT_CLAUSES_SUBS 0
#define PRINT_CLAUSE_COMMENTS_SUBS 0
void SATSubsumptionAndResolution::MatchSet::indexMatrix()
{
if (_matchesByJ.size())
return;
ASS_EQ(_matchesByJ.size(), 0)
ASS_EQ(_indexI.size(), 0)
ASS_EQ(_indexJ.size(), 0)
for (Match match : _matchesByI)
_matchesByJ.push_back(match);
std::sort(
_matchesByJ.begin(),
_matchesByJ.end(),
[](Match left, Match right) { return left.j < right.j; });
for (unsigned i = 0, idx = 0; i < _m; i++) {
_indexI.push_back(idx);
while (idx < _matchesByI.size() && _matchesByI[idx].i == i)
idx++;
}
_indexI.push_back(_matchesByI.size());
for (unsigned j = 0, idx = 0; j < _n; j++) {
_indexJ.push_back(idx);
while (idx < _matchesByJ.size() && _matchesByJ[idx].j == j)
idx++;
}
_indexJ.push_back(_matchesByJ.size());
}
bool SATSubsumptionAndResolution::MatchSet::hasPositiveMatchJ(unsigned j)
{
ASS(j < _n)
return (_jStates[j / 4] & (1 << (2 * (j % 4)))) != 0;
}
bool SATSubsumptionAndResolution::MatchSet::hasNegativeMatchJ(unsigned j)
{
ASS(j < _n)
return (_jStates[j / 4] & (2 << (2 * (j % 4)))) != 0;
}
void SATSubsumptionAndResolution::loadProblem(Clause* sidePremise,
Clause* mainPremise)
{
ASS(sidePremise)
ASS(mainPremise)
#if VDEBUG
static DHSet<Literal*> lits;
lits.reset();
for (unsigned i = 0; i < sidePremise->length(); i++)
if (!lits.insert((*sidePremise)[i]))
ASS(false)
lits.reset();
for (unsigned i = 0; i < mainPremise->length(); i++)
if (!lits.insert((*mainPremise)[i]))
ASS(false)
#endif
#if PRINT_CLAUSES_SUBS
cout << "----------------------------------------------" << endl;
cout << "Setting up problem " << sidePremise->toString() << " " << mainPremise->toString() << endl;
#endif
_sidePremise = sidePremise;
_mainPremise = mainPremise;
_m = sidePremise->length();
_n = mainPremise->length();
_matchSet.clear();
_matchSet.resize(_m, _n);
_subsumptionImpossible = false;
_srImpossible = false;
_solver.clear();
_bindingsManager.clear();
}
bool SATSubsumptionAndResolution::pruneSubsumption()
{
ASS(_sidePremise)
ASS(_mainPremise)
if (_sidePremise->length() > _mainPremise->length()) {
_subsumptionImpossible = true;
return true;
}
auto& headerMultiset = _pruneStorage;
prune_t& timestamp = _pruneTimestamp;
static_assert(
std::is_same<std::remove_reference_t<decltype(timestamp)>,
std::remove_reference_t<decltype(headerMultiset)>::value_type>::value,
"timestamp and storage should be the same type");
headerMultiset.resize(2 * env.signature->predicates(), 0);
ASS(std::all_of(headerMultiset.begin(), headerMultiset.end(), [&](prune_t x) { return x <= timestamp; }))
if (isAdditionOverflow<prune_t>(timestamp, _mainPremise->length())) {
std::fill(headerMultiset.begin(), headerMultiset.end(), 0);
timestamp = 0;
}
prune_t const zero = timestamp;
timestamp += _mainPremise->length();
ASS(std::all_of(headerMultiset.begin(), headerMultiset.end(), [&](prune_t x) { return x <= zero; }))
for (unsigned i = 0; i < _mainPremise->length(); i++) {
unsigned const hdr = (*_mainPremise)[i]->header();
headerMultiset[hdr] = std::max(headerMultiset[hdr], zero) + 1;
}
for (unsigned j = 0; j < _sidePremise->length(); j++) {
unsigned const hdr = (*_sidePremise)[j]->header();
if (headerMultiset[hdr] <= zero) {
_subsumptionImpossible = true;
return true;
}
headerMultiset[hdr]--;
}
ASS(!pruneSubsumptionResolution())
return false;
}
bool SATSubsumptionAndResolution::pruneSubsumptionResolution()
{
ASS(_sidePremise)
ASS(_mainPremise)
auto& functorSet = _pruneStorage;
auto& timestamp = _pruneTimestamp;
functorSet.resize(env.signature->predicates(), 0);
ASS(std::all_of(functorSet.begin(), functorSet.end(), [&](prune_t x) { return x <= timestamp; }))
timestamp++;
if (timestamp == 0) {
timestamp++;
std::fill(functorSet.begin(), functorSet.end(), 0);
}
ASS(std::all_of(functorSet.begin(), functorSet.end(), [&](prune_t x) { return x < timestamp; }));
for (unsigned i = 0; i < _mainPremise->length(); i++)
functorSet[(*_mainPremise)[i]->functor()] = timestamp;
for (unsigned j = 0; j < _sidePremise->length(); j++)
if (functorSet[(*_sidePremise)[j]->functor()] != timestamp)
return true;
return false;
}
void SATSubsumptionAndResolution::addBinding(BindingsManager::Binder* binder,
unsigned i,
unsigned j,
bool polarity,
bool isNullary)
{
ASS(binder || isNullary)
ASS(i < _m)
ASS(j < _n)
ASS_EQ((*_sidePremise)[i]->functor(), (*_mainPremise)[j]->functor())
ASS_EQ((*_sidePremise)[i]->polarity() == (*_mainPremise)[j]->polarity(), polarity)
subsat::Var satVar = _solver.new_variable();
#if PRINT_CLAUSES_SUBS
cout << satVar << " -> (" << (*_sidePremise)[i]->toString() << " " << (*_mainPremise)[j]->toString() << " " << (polarity ? "+" : "-") << ")" << endl;
#endif
_matchSet.addMatch(i, j, polarity, satVar);
if (!isNullary)
_bindingsManager.commit_bindings(*binder, satVar);
}
bool SATSubsumptionAndResolution::checkAndAddMatch(Literal* l_i,
Literal* m_j,
unsigned i,
unsigned j,
bool polarity)
{
ASS(l_i)
ASS(m_j)
ASS_EQ((*_sidePremise)[i], l_i)
ASS_EQ((*_mainPremise)[j], m_j)
ASS_EQ(l_i->functor(), m_j->functor())
ASS_EQ(l_i->polarity() == m_j->polarity(), polarity)
bool match = false;
{
auto binder = _bindingsManager.start_binder();
if (MatchingUtils::matchArgs(l_i, m_j, binder)) {
addBinding(&binder, i, j, polarity, false);
match = true;
}
}
if (l_i->isEquality()) {
auto binder = _bindingsManager.start_binder();
if (MatchingUtils::matchReversedArgs(l_i, m_j, binder)) {
addBinding(&binder, i, j, polarity, false);
match = true;
}
}
return match;
}
bool SATSubsumptionAndResolution::fillMatchesS()
{
ASS(_sidePremise)
ASS(_mainPremise)
ASS_G(_m, 0)
ASS_G(_n, 0)
ASS_EQ(_matchSet._m, _m)
ASS_EQ(_matchSet._n, _n)
Literal* l_i, * m_j;
for (unsigned i = 0; i < _m; ++i) {
l_i = _sidePremise->literals()[i];
bool foundMatch = false;
for (unsigned j = 0; j < _n; ++j) {
m_j = _mainPremise->literals()[j];
if (l_i->functor() != m_j->functor() || l_i->polarity() != m_j->polarity()) {
continue;
}
if (l_i->arity() == 0) {
ASS(m_j->arity() == 0)
ASS(l_i->functor() == m_j->functor())
addBinding(nullptr, i, j, true, true);
foundMatch = true;
continue;
}
foundMatch = checkAndAddMatch(l_i, m_j, i, j, true) || foundMatch;
}
if (!foundMatch) {
_subsumptionImpossible = true;
return false;
} }
return true;
}
void SATSubsumptionAndResolution::fillMatchesSR(unsigned litToRemove)
{
ASS(_sidePremise)
ASS(_mainPremise)
ASS_G(_m, 0)
ASS_G(_n, 0)
ASS_EQ(_matchSet._m, _m)
ASS_EQ(_matchSet._n, _n)
bool clauseHasNegativeMatch = false;
Literal* firstOnlyNegativeMatch = nullptr;
for (unsigned i = 0; i < _m; ++i) {
Literal* l_i = _sidePremise->literals()[i];
bool literalHasPositiveMatch = false;
bool literalHasNegativeMatch = false;
for (unsigned j = 0; j < _n; ++j) {
Literal* m_j = _mainPremise->literals()[j];
if (l_i->functor() != m_j->functor())
continue;
if (l_i->arity() == 0) {
ASS(m_j->arity() == 0)
ASS(l_i->functor() == m_j->functor())
if (l_i->polarity() == m_j->polarity()) {
addBinding(nullptr, i, j, true, true);
literalHasPositiveMatch = true;
continue;
}
if (litToRemove != 0xFFFFFFFF && j != litToRemove)
continue;
addBinding(nullptr, i, j, false, true);
clauseHasNegativeMatch = true;
literalHasNegativeMatch = true;
continue;
}
if (l_i->polarity() == m_j->polarity()) {
literalHasPositiveMatch = checkAndAddMatch(l_i, m_j, i, j, true) || literalHasPositiveMatch;
continue;
}
if (litToRemove != 0xFFFFFFFF && j != litToRemove)
continue;
literalHasNegativeMatch = checkAndAddMatch(l_i, m_j, i, j, false) || literalHasNegativeMatch;
clauseHasNegativeMatch |= literalHasNegativeMatch;
}
if (!literalHasPositiveMatch) {
_subsumptionImpossible = true;
if (!literalHasNegativeMatch) {
_srImpossible = true;
return;
}
if (!firstOnlyNegativeMatch)
firstOnlyNegativeMatch = l_i;
else if (firstOnlyNegativeMatch->header() != l_i->header()) {
_srImpossible = true;
return;
}
} }
if (!clauseHasNegativeMatch)
_srImpossible = true;
}
bool SATSubsumptionAndResolution::cnfForSubsumption()
{
ASS(_sidePremise)
ASS(_mainPremise)
ASS_GE(_matchSet.allMatches().size(), _sidePremise->length())
ASS_G(_sidePremise->length(), 0)
ASS(!_subsumptionImpossible)
_matchSet.indexMatrix();
Solver& solver = _solver;
for (unsigned i = 0; i < _m; ++i) {
solver.constraint_start();
for (Match match : _matchSet.getIMatches(i)) {
if (match.polarity) {
solver.constraint_push_literal(match.var);
}
}
auto handle = solver.constraint_end();
solver.add_clause_unsafe(handle);
}
for (unsigned j = 0; j < _n; ++j) {
solver.constraint_start();
for (Match match : _matchSet.getJMatches(j)) {
if (match.polarity) {
solver.constraint_push_literal(match.var);
}
}
auto handle = solver.constraint_end();
solver.add_atmostone_constraint_unsafe(handle);
}
return !solver.inconsistent();
}
static std::vector<pair<unsigned, subsat::Var>> atMostOneVars;
bool SATSubsumptionAndResolution::cnfForSubsumptionResolution()
{
ASS(_sidePremise)
ASS(_mainPremise)
ASS_GE(_matchSet.allMatches().size(), _sidePremise->length())
atMostOneVars.clear();
_matchSet.indexMatrix();
Solver& solver = _solver;
#if PRINT_CLAUSE_COMMENTS_SUBS
cout << "Existence" << endl;
#endif
#if PRINT_CLAUSES_SUBS
string s = "";
#endif
solver.constraint_start();
for (unsigned j = 0; j < _n; ++j) {
if (!_matchSet.hasNegativeMatchJ(j))
continue;
bool one_match_found = false;
subsat::Var negative_match_var = subsat::Var(0xffffffff);
for (Match match : _matchSet.getJMatches(j)) {
if (!match.polarity) {
one_match_found = !one_match_found;
if (!one_match_found)
break;
negative_match_var = match.var;
}
}
if (one_match_found) {
ASS(!_matchSet.getMatchForVar(negative_match_var).polarity)
atMostOneVars.push_back(make_pair(j, negative_match_var));
solver.constraint_push_literal(negative_match_var);
#if PRINT_CLAUSES_SUBS
s += Int::toString(negative_match_var.index()) + " ∨ ";
#endif
continue;
}
subsat::Var c_j = solver.new_variable();
atMostOneVars.push_back(make_pair(j, c_j));
solver.constraint_push_literal(c_j);
#if PRINT_CLAUSES_SUBS
s += Int::toString(c_j.index()) + " ∨ ";
#endif
}
ASS(!atMostOneVars.empty())
auto build = solver.constraint_end();
solver.add_clause_unsafe(build);
#if PRINT_CLAUSES_SUBS
cout << s.substr(0, s.size() - 3) << endl;
#endif
#if PRINT_CLAUSE_COMMENTS_SUBS
cout << "Uniqueness" << endl;
#endif
#if PRINT_CLAUSES_SUBS
cout << "AtMostOne(";
#endif
solver.constraint_start();
for (auto var : atMostOneVars) {
solver.constraint_push_literal(var.second);
#if PRINT_CLAUSES_SUBS
cout << var.second;
if (var != atMostOneVars.back()) {
cout << ", ";
}
#endif
} build = solver.constraint_end();
solver.add_atmostone_constraint(build);
#if PRINT_CLAUSES_SUBS
cout << ")" << endl;
#endif
#if PRINT_CLAUSE_COMMENTS_SUBS
cout << "Completeness" << endl;
#endif
for (unsigned i = 0; i < _m; ++i) {
solver.constraint_start();
Slice<Match> matches = _matchSet.getIMatches(i);
for (Match match : matches) {
#if PRINT_CLAUSES_SUBS
cout << match.var;
if (match != matches.back()) {
cout << " ∨ ";
}
#endif
solver.constraint_push_literal(match.var);
}
auto build = solver.constraint_end();
solver.add_clause_unsafe(build);
#if PRINT_CLAUSES_SUBS
cout << endl;
#endif
}
#if PRINT_CLAUSE_COMMENTS_SUBS
cout << "Coherence" << endl;
#endif
for (auto var : atMostOneVars) {
unsigned j = var.first;
subsat::Var c_j = var.second;
if (_matchSet.hasPositiveMatchJ(j)) {
for (Match match : _matchSet.getJMatches(j)) {
subsat::Var b_ij = match.var;
if (match.polarity) {
solver.constraint_start();
solver.constraint_push_literal(~c_j);
solver.constraint_push_literal(~b_ij);
build = solver.constraint_end();
solver.add_clause_unsafe(build);
#if PRINT_CLAUSES_SUBS
cout << ~c_j << " ∨ " << ~b_ij << endl;
#endif
} } } }
#if PRINT_CLAUSE_COMMENTS_SUBS
cout << "Stucturality" << endl;
#endif
for (auto& pair : atMostOneVars) {
unsigned j = pair.first;
subsat::Var c_j = pair.second;
if (_matchSet.isMatchVar(c_j))
continue;
Slice<Match> matches = _matchSet.getJMatches(j);
solver.constraint_start();
solver.constraint_push_literal(~c_j);
#if PRINT_CLAUSES_SUBS
cout << ~c_j;
#endif
for (Match match : matches) {
if (!match.polarity) {
solver.constraint_push_literal(match.var);
#if PRINT_CLAUSES_SUBS
cout << " ∨ " << match.var;
#endif
}
} #if PRINT_CLAUSES_SUBS
cout << endl;
#endif
auto build = solver.constraint_end();
solver.add_clause_unsafe(build);
for (Match match : matches) {
if (!match.polarity) {
solver.constraint_start();
solver.constraint_push_literal(c_j);
solver.constraint_push_literal(~match.var);
auto build = solver.constraint_end();
solver.add_clause_unsafe(build);
#if PRINT_CLAUSES_SUBS
cout << c_j << " ∨ " << ~match.var << endl;
#endif
} } } return !solver.inconsistent();
}
Clause* SATSubsumptionAndResolution::getSubsumptionResolutionConclusion(Clause* mainPremise,
Literal* m_j,
Clause* sidePremise,
bool forward)
{
RStack<Literal*> resLits;
int mlen = mainPremise->length();
for (int i = 0; i < mlen; i++) {
Literal* curr = (*mainPremise)[i];
if (curr == m_j)
continue;
resLits->push(curr);
}
return Clause::fromStack(*resLits,SimplifyingInference2(forward
? InferenceRule::FORWARD_SUBSUMPTION_RESOLUTION
: InferenceRule::BACKWARD_SUBSUMPTION_RESOLUTION, mainPremise, sidePremise));
}
Clause* SATSubsumptionAndResolution::generateConclusion(bool forward)
{
ASS(_sidePremise)
ASS(_mainPremise)
ASS(_m > 0)
ASS(_n > 0)
ASS_EQ(_matchSet._m, _m)
ASS_EQ(_matchSet._n, _n)
ASS(_model.size() > 0)
ASS_GE(_matchSet.allMatches().size(), _sidePremise->length())
#if VDEBUG
unsigned j = INVALID;
for (subsat::Lit lit : _model) {
if (lit.is_positive()) {
if (_matchSet.isMatchVar(lit.var())) {
Match match = _matchSet.getMatchForVar(lit.var());
if (!match.polarity) {
if (j == INVALID)
j = match.j;
else
ASS(j == match.j)
}
}
}
}
#endif
unsigned toRemove = INVALID;
#if PRINT_CLAUSES_SUBS
cout << "Model: ";
for (subsat::Lit lit : _model) {
cout << lit << " ";
}
cout << endl;
#endif
for (subsat::Lit lit : _model) {
if (lit.is_positive()) {
if (_matchSet.isMatchVar(lit.var())) {
Match match = _matchSet.getMatchForVar(lit.var());
if (!match.polarity) {
toRemove = match.j;
break;
}
}
}
}
ASS_EQ(_n, _mainPremise->size())
ASS(toRemove != INVALID)
return SATSubsumptionAndResolution::getSubsumptionResolutionConclusion(_mainPremise, (*_mainPremise)[toRemove], _sidePremise, forward);
}
bool SATSubsumptionAndResolution::checkSubsumption(Clause* sidePremise,
Clause* mainPremise,
bool setSR)
{
ASS(sidePremise)
ASS(mainPremise)
loadProblem(sidePremise, mainPremise);
if (setSR) {
_srImpossible = pruneSubsumptionResolution();
_subsumptionImpossible = _srImpossible || pruneSubsumption();
if (_srImpossible) {
ASS(_subsumptionImpossible);
return false;
}
else {
ASS(!_srImpossible)
fillMatchesSR();
}
if (_subsumptionImpossible)
return false;
} else if (pruneSubsumption() || !fillMatchesS())
return false;
ASS_GE(_matchSet.allMatches().size(), _sidePremise->length())
if (!cnfForSubsumption())
return false;
_solver.theory().setBindings(&_bindingsManager);
auto const result = _solver.solve();
bool const subsumed = result == subsat::Result::Sat;
return subsumed;
}
Clause* SATSubsumptionAndResolution::checkSubsumptionResolution(Clause* sidePremise,
Clause* mainPremise,
bool forward,
bool usePreviousSetUp)
{
ASS(sidePremise)
ASS(mainPremise)
if (usePreviousSetUp) {
ASS(_sidePremise == sidePremise)
ASS(_mainPremise == mainPremise)
if (_srImpossible) {
#if PRINT_CLAUSES_SUBS
cout << "SR impossible" << endl;
#endif
return nullptr;
}
ASS_GE(_matchSet.allMatches().size(), _sidePremise->length())
_solver.clear_constraints();
}
else {
loadProblem(sidePremise, mainPremise);
if (pruneSubsumptionResolution()) {
#if PRINT_CLAUSES_SUBS
cout << "SR pruned" << endl;
#endif
return nullptr;
}
fillMatchesSR();
if (_srImpossible) {
#if PRINT_CLAUSES_SUBS
cout << "SR impossible" << endl;
#endif
return nullptr;
}
}
if (!cnfForSubsumptionResolution()) {
#if PRINT_CLAUSES_SUBS
cout << "CNF building failed" << endl;
#endif
return nullptr;
}
if (_solver.theory().empty()) {
_solver.theory().setBindings(&_bindingsManager);
}
Clause* conclusion = nullptr;
auto const result = _solver.solve();
if (result == subsat::Result::Sat) {
#if PRINT_CLAUSES_SUBS
cout << "SAT solver succeeded" << endl;
#endif
_model.clear();
_solver.get_model(_model);
conclusion = generateConclusion(forward);
}
#if PRINT_CLAUSES_SUBS
else
cout << "SAT solver failed (" << result << ")" << endl;
#endif
return conclusion;
}
bool SATSubsumption::SATSubsumptionAndResolution::checkSubsumptionResolutionWithLiteral(Kernel::Clause* sidePremise, Kernel::Clause* mainPremise, unsigned resolutionLiteral)
{
loadProblem(sidePremise, mainPremise);
if (pruneSubsumptionResolution()) {
return false;
}
fillMatchesSR(resolutionLiteral);
if (_srImpossible) {
return false;
}
if (!cnfForSubsumptionResolution()) {
return false;
}
ASS(_solver.theory().empty())
_solver.theory().setBindings(&_bindingsManager);
_model.clear();
return (_solver.solve() == subsat::Result::Sat);
}
Substitution SATSubsumption::SATSubsumptionAndResolution::getBindingsForSubsumptionResolutionWithLiteral()
{
Substitution subst;
_solver.get_model(_model);
for(auto lit : _model) {
if(lit.is_negative())
continue;
auto var = lit.var();
if(!_matchSet.isMatchVar(var))
continue;
Match match = _matchSet.getMatchForVar(var);
Literal *l = (*_sidePremise)[match.i];
Literal *k = (*_mainPremise)[match.j];
if(!l->arity())
continue;
bool reverseArgs = false;
if(l->isEquality() && MatchingUtils::matchReversedArgs(l, k)) {
if(MatchingUtils::matchArgs(l, k)) {
unsigned index = var.index();
if(index) {
Match previous = _matchSet.getMatchForVar(subsat::Var(index - 1));
reverseArgs = match.i == previous.i && match.j == previous.j;
}
}
else
reverseArgs = true;
}
if(reverseArgs) {
ALWAYS(MatchingUtils::matchReversedArgs(l, k, subst));
}
else {
ALWAYS(MatchingUtils::matchArgs(l, k, subst));
}
}
return subst;
}