/*****************************************************************************
CryptoMiniSat -- Copyright (c) 2009 Mate Soos
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
******************************************************************************/
#include "VarReplacer.h"
#include
#include
#include
#include "ClauseCleaner.h"
#include "time_mem.h"
#include "DataSync.h"
//#define VERBOSE_DEBUG
//#define DEBUG_REPLACER
//#define REPLACE_STATISTICS
//#define DEBUG_BIN_REPLACER
using namespace CMSat;
VarReplacer::VarReplacer(Solver& _solver) :
replacedLits(0)
, replacedVars(0)
, lastReplacedVars(0)
, solver(_solver)
{
}
VarReplacer::~VarReplacer()
{
}
/**
@brief Replaces variables, clears internal clauses, and reports stats
When replacing, it is imperative not to make variables decision variables
which have been removed by other methods:
\li variable removal at the xor-sphere
\li disconnected component finding and solving
\li variable elimination
NOTE: If any new such algoirhtms are added, this part MUST be updated such
that problems don't creep up
*/
bool VarReplacer::performReplaceInternal()
{
#ifdef VERBOSE_DEBUG
cout << "PerformReplacInternal started." << endl;
//solver.printAllClauses();
#endif
double time = cpuTime();
#ifdef REPLACE_STATISTICS
uint32_t numRedir = 0;
for (uint32_t i = 0; i < table.size(); i++) {
if (table[i].var() != i)
numRedir++;
}
std::cout << "Number of trees:" << reverseTable.size() << std::endl;
std::cout << "Number of redirected nodes:" << numRedir << std::endl;
#endif //REPLACE_STATISTICS
solver.clauseCleaner->removeAndCleanAll(true);
if (!solver.ok) return false;
solver.testAllClauseAttach();
std::fill(cannot_eliminate.getData(), cannot_eliminate.getDataEnd(), false);
#ifdef VERBOSE_DEBUG
{
uint32_t i = 0;
for (vector::const_iterator it = table.begin(); it != table.end(); it++, i++) {
if (it->var() == i) continue;
cout << "Replacing var " << i+1 << " with Lit " << *it << endl;
}
}
#endif
Var var = 0;
const vec& removedVars = solver.xorSubsumer->getVarElimed();
const vec& removedVars3 = solver.subsumer->getVarElimed();
for (vector::const_iterator it = table.begin(); it != table.end(); it++, var++) {
if (it->var() == var
|| removedVars[it->var()]
|| removedVars3[it->var()]
) continue;
#ifdef VERBOSE_DEBUG
cout << "Setting var " << var+1 << " to a non-decision var" << endl;
#endif
bool wasDecisionVar = solver.decision_var[var];
solver.setDecisionVar(var, false);
//cannot_eliminate[var] = true;
solver.setDecisionVar(it->var(), true);
assert(!removedVars[var]);
assert(!removedVars3[var]);
uint32_t& activity1 = solver.activity[var];
uint32_t& activity2 = solver.activity[it->var()];
if (wasDecisionVar && activity1 > activity2) {
activity2 = activity1;
solver.order_heap.update(it->var());
solver.polarity[it->var()] = ((bool)solver.polarity[var]) ^ it->sign();
}
activity1 = 0.0;
solver.order_heap.update(var);
}
assert(solver.order_heap.heapProperty());
uint32_t thisTimeReplaced = replacedVars -lastReplacedVars;
lastReplacedVars = replacedVars;
solver.testAllClauseAttach();
assert(solver.qhead == solver.trail.size());
solver.countNumBinClauses(true, false);
solver.countNumBinClauses(false, true);
if (!replaceBins()) goto end;
if (!replace_set(solver.clauses)) goto end;
if (!replace_set(solver.learnts)) goto end;
if (!replace_set(solver.xorclauses)) goto end;
solver.testAllClauseAttach();
end:
assert(solver.qhead == solver.trail.size() || !solver.ok);
if (solver.conf.verbosity >= 3) {
std::cout << "c Replacing "
<< std::setw(8) << thisTimeReplaced << " vars"
<< " Replaced " << std::setw(8) << replacedLits<< " lits"
<< " Time: " << std::setw(8) << std::fixed << std::setprecision(2)
<< cpuTime()-time << " s "
<< std::endl;
}
replacedLits = 0;
solver.order_heap.filter(Solver::VarFilter(solver));
return solver.ok;
}
/**
@brief Replaces vars in xorclauses
*/
bool VarReplacer::replace_set(vec& cs)
{
XorClause **a = cs.getData();
XorClause **r = a;
for (XorClause **end = a + cs.size(); r != end; r++) {
XorClause& c = **r;
bool changed = false;
Var origVar1 = c[0].var();
Var origVar2 = c[1].var();
for (Lit *l = &c[0], *end2 = l + c.size(); l != end2; l++) {
Lit newlit = table[l->var()];
if (newlit.var() != l->var()) {
changed = true;
*l = Lit(newlit.var(), false);
c.invert(newlit.sign());
replacedLits++;
}
}
if (changed && handleUpdatedClause(c, origVar1, origVar2)) {
if (!solver.ok) {
#ifdef VERBOSE_DEBUG
cout << "contradiction while replacing lits in xor clause" << std::endl;
#endif
for(;r != end; r++) solver.clauseAllocator.clauseFree(*r);
cs.shrink(r-a);
return false;
}
//solver.clauseAllocator.clauseFree(&c);
c.setRemoved();
solver.freeLater.push(&c);
} else {
#ifdef SILENT_DEBUG
uint32_t numUndef = 0;
for (uint32_t i = 0; i < c.size(); i++) {
if (solver.value(c[i]) == l_Undef) numUndef++;
}
assert(numUndef >= 2 || numUndef == 0);
#endif
*a++ = *r;
}
}
cs.shrink(r-a);
return solver.ok;
}
/**
@brief Helper function for replace_set()
*/
bool VarReplacer::handleUpdatedClause(XorClause& c, const Var origVar1, const Var origVar2)
{
uint32_t origSize = c.size();
std::sort(c.getData(), c.getDataEnd());
Lit p;
uint32_t i, j;
for (i = j = 0, p = lit_Undef; i != c.size(); i++) {
if (c[i].var() == p.var()) {
//added, but easily removed
j--;
p = lit_Undef;
if (!solver.assigns[c[i].var()].isUndef())
c.invert(solver.assigns[c[i].var()].getBool());
} else if (solver.assigns[c[i].var()].isUndef()) //just add
c[j++] = p = c[i];
else c.invert(solver.assigns[c[i].var()].getBool()); //modify xorEqualFalse instead of adding
}
c.shrink(i - j);
//Even if i==j, the clause *has* changed
c.setChanged();
#ifdef VERBOSE_DEBUG
cout << "xor-clause after replacing: ";
c.plainPrint();
#endif
switch (c.size()) {
case 0:
solver.detachModifiedClause(origVar1, origVar2, origSize, &c);
if (!c.xorEqualFalse()) {
solver.ok = false;
}
return true;
case 1:
solver.detachModifiedClause(origVar1, origVar2, origSize, &c);
solver.uncheckedEnqueue(Lit(c[0].var(), c.xorEqualFalse()));
solver.ok = (solver.propagate().isNULL());
return true;
case 2: {
solver.detachModifiedClause(origVar1, origVar2, origSize, &c);
c[0] = c[0].unsign() ^ c.xorEqualFalse();
c[1] = c[1].unsign();
addBinaryXorClause(c[0], c[1]);
return true;
}
default:
solver.detachModifiedClause(origVar1, origVar2, origSize, &c);
solver.attachClause(c);
return false;
}
assert(false);
return false;
}
bool VarReplacer::replaceBins()
{
#ifdef DEBUG_BIN_REPLACER
vec removed(solver.nVars()*2, 0);
uint32_t replacedLitsBefore = replacedLits;
#endif
uint32_t removedLearnt = 0;
uint32_t removedNonLearnt = 0;
uint32_t wsLit = 0;
for (vec *it = solver.watches.getData(), *end = solver.watches.getDataEnd(); it != end; it++, wsLit++) {
Lit lit1 = ~Lit::toLit(wsLit);
vec& ws = *it;
vec::iterator i = ws.getData();
vec::iterator j = i;
for (vec::iterator end2 = ws.getDataEnd(); i != end2; i++) {
if (!i->isBinary()) {
*j++ = *i;
continue;
}
//std::cout << "bin: " << lit1 << " , " << i->getOtherLit() << " learnt : " << (i->isLearnt()) << std::endl;
Lit thisLit1 = lit1;
Lit lit2 = i->getOtherLit();
assert(thisLit1.var() != lit2.var());
if (table[lit2.var()].var() != lit2.var()) {
lit2 = table[lit2.var()] ^ lit2.sign();
i->setOtherLit(lit2);
replacedLits++;
}
bool changedMain = false;
if (table[thisLit1.var()].var() != thisLit1.var()) {
thisLit1 = table[thisLit1.var()] ^ thisLit1.sign();
replacedLits++;
changedMain = true;
}
if (thisLit1 == lit2) {
if (solver.value(lit2) == l_Undef) {
solver.uncheckedEnqueue(lit2);
} else if (solver.value(lit2) == l_False) {
#ifdef VERBOSE_DEBUG
std::cout << "Contradiction during replacement of lits in binary clause" << std::endl;
#endif
solver.ok = false;
}
#ifdef DEBUG_BIN_REPLACER
removed[lit1.toInt()]++;
removed[origLit2.toInt()]++;
#endif
if (i->getLearnt()) removedLearnt++;
else removedNonLearnt++;
continue;
}
if (thisLit1 == ~lit2) {
#ifdef DEBUG_BIN_REPLACER
removed[lit1.toInt()]++;
removed[origLit2.toInt()]++;
#endif
if (i->getLearnt()) removedLearnt++;
else removedNonLearnt++;
continue;
}
if (changedMain) {
solver.watches[(~thisLit1).toInt()].push(*i);
} else {
*j++ = *i;
}
}
ws.shrink_(i-j);
}
#ifdef DEBUG_BIN_REPLACER
for (uint32_t i = 0; i < removed.size(); i++) {
if (removed[i] % 2 != 0) {
std::cout << "suspicious: " << Lit::toLit(i) << std::endl;
std::cout << "num: " << removed[i] << std::endl;
}
}
std::cout << "replacedLitsdiff: " << replacedLits - replacedLitsBefore << std::endl;
std::cout << "removedLearnt: " << removedLearnt << std::endl;
std::cout << "removedNonLearnt: " << removedNonLearnt << std::endl;
#endif
assert(removedLearnt % 2 == 0);
assert(removedNonLearnt % 2 == 0);
solver.learnts_literals -= removedLearnt;
solver.clauses_literals -= removedNonLearnt;
solver.numBins -= (removedLearnt + removedNonLearnt)/2;
if (solver.ok) solver.ok = (solver.propagate().isNULL());
return solver.ok;
}
/**
@brief Replaces variables in normal clauses
*/
bool VarReplacer::replace_set(vec& cs)
{
Clause **a = cs.getData();
Clause **r = a;
for (Clause **end = a + cs.size(); r != end; r++) {
Clause& c = **r;
assert(c.size() > 2);
bool changed = false;
Lit origLit1 = c[0];
Lit origLit2 = c[1];
Lit origLit3 = (c.size() == 3) ? c[2] : lit_Undef;
for (Lit *l = c.getData(), *end2 = l + c.size(); l != end2; l++) {
if (table[l->var()].var() != l->var()) {
changed = true;
*l = table[l->var()] ^ l->sign();
replacedLits++;
}
}
if (changed && handleUpdatedClause(c, origLit1, origLit2, origLit3)) {
if (!solver.ok) {
#ifdef VERBOSE_DEBUG
cout << "contradiction while replacing lits in normal clause" << std::endl;
#endif
for(;r != end; r++) solver.clauseAllocator.clauseFree(*r);
cs.shrink(r-a);
return false;
}
} else {
*a++ = *r;
}
}
cs.shrink(r-a);
return solver.ok;
}
/**
@brief Helper function for replace_set()
*/
bool VarReplacer::handleUpdatedClause(Clause& c, const Lit origLit1, const Lit origLit2, const Lit origLit3)
{
bool satisfied = false;
std::sort(c.getData(), c.getData() + c.size());
Lit p;
uint32_t i, j;
const uint32_t origSize = c.size();
for (i = j = 0, p = lit_Undef; i != origSize; i++) {
if (solver.value(c[i]) == l_True || c[i] == ~p) {
satisfied = true;
break;
}
else if (solver.value(c[i]) != l_False && c[i] != p)
c[j++] = p = c[i];
}
c.shrink(i - j);
c.setChanged();
solver.detachModifiedClause(origLit1, origLit2, origLit3, origSize, &c);
#ifdef VERBOSE_DEBUG
cout << "clause after replacing: ";
c.plainPrint();
#endif
if (satisfied) return true;
switch(c.size()) {
case 0:
solver.ok = false;
return true;
case 1 :
solver.uncheckedEnqueue(c[0]);
solver.ok = (solver.propagate().isNULL());
return true;
case 2:
solver.attachBinClause(c[0], c[1], c.learnt());
solver.numNewBin++;
solver.dataSync->signalNewBinClause(c);
return true;
default:
solver.attachClause(c);
return false;
}
assert(false);
return false;
}
/**
@brief Returns variables that have been replaced
*/
vector VarReplacer::getReplacingVars() const
{
vector replacingVars;
for(map >::const_iterator it = reverseTable.begin(), end = reverseTable.end(); it != end; it++) {
replacingVars.push_back(it->first);
}
return replacingVars;
}
/**
@brief Given a partial model, it tries to extend it to variables that have been replaced
Cannot extend it fully, because it might be that a replaced d&f, but a was
later variable-eliminated. That's where extendModelImpossible() comes in
*/
void VarReplacer::extendModelPossible() const
{
#ifdef VERBOSE_DEBUG
std::cout << "extendModelPossible() called" << std::endl;
#endif //VERBOSE_DEBUG
uint32_t i = 0;
for (vector::const_iterator it = table.begin(); it != table.end(); it++, i++) {
if (it->var() == i) continue;
#ifdef VERBOSE_DEBUG
cout << "Extending model: var "; solver.printLit(Lit(i, false));
cout << " to "; solver.printLit(*it);
cout << endl;
#endif
if (solver.assigns[it->var()] != l_Undef) {
if (solver.assigns[i] == l_Undef) {
bool val = (solver.assigns[it->var()] == l_False);
solver.uncheckedEnqueue(Lit(i, val ^ it->sign()));
} else {
assert(solver.assigns[i].getBool() == (solver.assigns[it->var()].getBool() ^ it->sign()));
}
}
solver.ok = (solver.propagate().isNULL());
assert(solver.ok);
}
}
/**
@brief Used when a variable was eliminated, but it replaced some other variables
This function will add to solver2 clauses that represent the relationship of
the variables to their replaced cousins. Then, calling solver2.solve() should
take care of everything
*/
void VarReplacer::extendModelImpossible(Solver& solver2) const
{
#ifdef VERBOSE_DEBUG
std::cout << "extendModelImpossible() called" << std::endl;
#endif //VERBOSE_DEBUG
vec tmpClause;
uint32_t i = 0;
for (vector::const_iterator it = table.begin(); it != table.end(); it++, i++) {
if (it->var() == i) continue;
if (solver.assigns[it->var()] == l_Undef) {
assert(solver.assigns[it->var()] == l_Undef);
assert(solver.assigns[i] == l_Undef);
tmpClause.clear();
tmpClause.push(Lit(it->var(), true));
tmpClause.push(Lit(i, it->sign()));
solver2.addClause(tmpClause);
assert(solver2.ok);
tmpClause.clear();
tmpClause.push(Lit(it->var(), false));
tmpClause.push(Lit(i, it->sign()^true));
solver2.addClause(tmpClause);
assert(solver2.ok);
}
}
}
/**
@brief Replaces two two vars in "ps" with one another. xorEqualFalse defines anti/equivalence
It can be tricky to do this. For example, if:
\li a replaces: b, c
\li f replaces: f, h
\li we just realised that c = h
This is the most difficult case, but there are other cases, e.g. if we already
know that c=h, in which case we don't do anything
@p ps must contain 2 variables(!), i.e literals with no sign
@p xorEqualFalse if True, the two variables are equivalent. Otherwise, they are antivalent
@p group of clause they have been inspired from. Sometimes makes no sense...
*/
template
bool VarReplacer::replace(T& ps, const bool xorEqualFalse, const bool addBinAsLearnt, const bool addToWatchLists)
{
#ifdef VERBOSE_DEBUG
std::cout << "replace() called with var " << ps[0].var()+1 << " and var " << ps[1].var()+1 << " with xorEqualFalse " << xorEqualFalse << std::endl;
#endif
assert(solver.decisionLevel() == 0);
assert(ps.size() == 2);
assert(!ps[0].sign());
assert(!ps[1].sign());
#ifdef DEBUG_REPLACER
assert(solver.assigns[ps[0].var()].isUndef());
assert(solver.assigns[ps[1].var()].isUndef());
assert(!solver.subsumer->getVarElimed()[ps[0].var()]);
assert(!solver.xorSubsumer->getVarElimed()[ps[0].var()]);
assert(!solver.subsumer->getVarElimed()[ps[1].var()]);
assert(!solver.xorSubsumer->getVarElimed()[ps[1].var()]);
#endif
//Detect circle
Lit lit1 = ps[0];
lit1 = table[lit1.var()];
Lit lit2 = ps[1];
lit2 = table[lit2.var()] ^ !xorEqualFalse;
//Already inside?
if (lit1.var() == lit2.var()) {
if (lit1.sign() != lit2.sign()) {
solver.ok = false;
return false;
}
return true;
}
#ifdef DEBUG_REPLACER
assert(!solver.subsumer->getVarElimed()[lit1.var()]);
assert(!solver.xorSubsumer->getVarElimed()[lit1.var()]);
assert(!solver.subsumer->getVarElimed()[lit2.var()]);
assert(!solver.xorSubsumer->getVarElimed()[lit2.var()]);
#endif
cannot_eliminate[lit1.var()] = true;
cannot_eliminate[lit2.var()] = true;
lbool val1 = solver.value(lit1);
lbool val2 = solver.value(lit2);
if (val1 != l_Undef && val2 != l_Undef) {
if (val1 != val2) {
solver.ok = false;
return false;
}
return true;
}
if ((val1 != l_Undef && val2 == l_Undef) || (val2 != l_Undef && val1 == l_Undef)) {
//exactly one l_Undef, exectly one l_True/l_False
if (val1 != l_Undef) solver.uncheckedEnqueue(lit2 ^ (val1 == l_False));
else solver.uncheckedEnqueue(lit1 ^ (val2 == l_False));
if (solver.ok) solver.ok = (solver.propagate().isNULL());
return solver.ok;
}
#ifdef DEBUG_REPLACER
assert(val1 == l_Undef && val2 == l_Undef);
#endif //DEBUG_REPLACER
if (addToWatchLists)
addBinaryXorClause(lit1, lit2 ^ true, addBinAsLearnt);
if (reverseTable.find(lit1.var()) == reverseTable.end()) {
reverseTable[lit2.var()].push_back(lit1.var());
table[lit1.var()] = lit2 ^ lit1.sign();
replacedVars++;
return true;
}
if (reverseTable.find(lit2.var()) == reverseTable.end()) {
reverseTable[lit1.var()].push_back(lit2.var());
table[lit2.var()] = lit1 ^ lit2.sign();
replacedVars++;
return true;
}
//both have children
setAllThatPointsHereTo(lit1.var(), lit2 ^ lit1.sign()); //erases reverseTable[lit1.var()]
replacedVars++;
return true;
}
template bool VarReplacer::replace(vec& ps, const bool xorEqualFalse, const bool addBinAsLearnt, const bool addToWatchLists);
template bool VarReplacer::replace(XorClause& ps, const bool xorEqualFalse, const bool addBinAsLearnt, const bool addToWatchLists);
/**
@brief Adds a binary xor to the internal/external clause set
It is added externally ONLY if we are in the middle of replacing clauses,
and a new binary xor just came up. That is a very strange and unfortunate
experience, as we cannot change the datastructures in the middle of replacement
so we add this to the binary clauses of Solver, and we recover it next time.
\todo Clean this messy internal/external thing using a better datastructure.
*/
void VarReplacer::addBinaryXorClause(Lit lit1, Lit lit2, const bool addBinAsLearnt)
{
solver.attachBinClause(lit1, lit2, addBinAsLearnt);
solver.dataSync->signalNewBinClause(lit1, lit2);
lit1 ^= true;
lit2 ^= true;
solver.attachBinClause(lit1, lit2, addBinAsLearnt);
solver.dataSync->signalNewBinClause(lit1, lit2);
}
/**
@brief Returns if we already know that var = lit
Also checks if var = ~lit, in which it sets solver.ok = false
*/
bool VarReplacer::alreadyIn(const Var var, const Lit lit)
{
Lit lit2 = table[var];
if (lit2.var() == lit.var()) {
if (lit2.sign() != lit.sign()) {
#ifdef VERBOSE_DEBUG
cout << "Inverted cycle in var-replacement -> UNSAT" << endl;
#endif
solver.ok = false;
}
return true;
}
lit2 = table[lit.var()];
if (lit2.var() == var) {
if (lit2.sign() != lit.sign()) {
#ifdef VERBOSE_DEBUG
cout << "Inverted cycle in var-replacement -> UNSAT" << endl;
#endif
solver.ok = false;
}
return true;
}
return false;
}
/**
@brief Changes internal graph to set everything that pointed to var to point to lit
*/
void VarReplacer::setAllThatPointsHereTo(const Var var, const Lit lit)
{
map >::iterator it = reverseTable.find(var);
if (it != reverseTable.end()) {
for(vector::const_iterator it2 = it->second.begin(), end = it->second.end(); it2 != end; it2++) {
assert(table[*it2].var() == var);
if (lit.var() != *it2) {
table[*it2] = lit ^ table[*it2].sign();
reverseTable[lit.var()].push_back(*it2);
}
}
reverseTable.erase(it);
}
table[var] = lit;
reverseTable[lit.var()].push_back(var);
}
void VarReplacer::newVar()
{
table.push_back(Lit(table.size(), false));
cannot_eliminate.push(false);
}