z3-z3-4.13.0.src.sat.sat_binspr.cpp Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2019 Microsoft Corporation
Module Name:
sat_binspr.cpp
Abstract:
Inprocessing step for creating SPR binary clauses.
Author:
Nikolaj Bjorner, Marijn Heule 2019-4-29
Notes:
L = { lit1, lit2 }
G := { touched_L(C) | C in F and C intersects with L, and not F|L |-_unit untouch_L(C) }
G & ~L is satisfiable
------------
Learn ~lit1 or ~lit2
Marijn's version:
L = { lit1, lit2 }
alpha = L + units in F|L
G := { touched_alpha(C) | C in F and C intersects with L, and not F|L |-_unit untouch_alpha(C) }
G & ~L is satisfiable
---------------------
Learn ~L
Alternative:
for p in literals:
push(1)
propagate(p)
candidates = literals \ units
for (C or p) in use(p) and candidates != empty:
push(1)
propagate(~C)
if inconsistent():
learn C (subsumes C or p)
else:
candidates' := C union ~(consequences of propagate(~C))
candidates := candidates' intersect candidates
pop(1)
for q in candidates:
add (~q or ~p)
pop(1)
The idea is that all clauses using p must satisfy
q in C or F|pq |-1 untouched(C)
The clauses that contain q are not restricted: We directly create G := (~p or ~q) & q, which is satisfiable
Let pqM |= F, we claim then that ~pqM |= F
- every clause (C or q) that contains q is satisfied by ~pqM
- every clause (C or p) that does not contain q positively, but contains p satisfies F|pq |-1 untouched(C)
Therefore pqM |= untouched(C) and therefore already M |= untouched(C)
- all other clauses are satisfied by pqM, but contain neither p, nor q,
so it is already the case that M satisfies the clause.
Alternative:
for p in literals:
push(1)
propagate(p)
candidates = {}
for (C or p) in use(p):
push(1)
propagate(~C)
if inconsistent():
learn C (subsumes C or p)
else:
candidates := candidates union C union ~(consequences of propagate(~C))
pop(1)
for q in candidates:
push(1)
propagate(q)
incons := true
for (C or p) in use(p) and incons:
push(1)
propagate(~C)
incons := inconsistent()
pop(1)
pop(1)
if incons:
add (~p or ~q)
pop(1)
The idea is similar to the previous alternative, but it allows a candidate to
not be directly unit derivable in all clauses C or p, but could be a failed literal
under the assumption ~C.
The motivation for this variant is that it is unlikely that we need failed literals to
close both (C_1 or p),..., (C_n or p) for all clauses containing p.
Alternative:
1. extract BIG
Use BIG to limit cone enumeration
2. for each literal lit:
enumerate k1 in cone of lit
enumerate lit2 in use(~k1)
check if cone of lit2 contains k2 such that lit1 in use(~k2)
--*/
#include "sat/sat_binspr.h"
#include "sat/sat_solver.h"
#include "sat/sat_big.h"
namespace sat {
struct binspr::report {
binspr& m_binspr;
stopwatch m_watch;
report(binspr& b):
m_binspr(b) {
m_watch.start();
}
~report() {
m_watch.stop();
unsigned nb = m_binspr.m_bin_clauses;
IF_VERBOSE(2, verbose_stream() << " (sat-binspr :binary " << nb << m_watch << ")\n");
}
};
void binspr::operator()() {
s = alloc(solver, m_solver.params(), m_solver.rlimit());
m_solver.pop_to_base_level();
s->copy(m_solver, true);
unsigned num = s->num_vars();
m_bin_clauses = 0;
report _rep(*this);
m_use_list.reset();
m_use_list.reserve(num * 2);
for (clause* c : s->m_clauses) {
if (!c->frozen() && !c->was_removed()) {
for (literal lit : *c) {
m_use_list[lit.index()].push_back(c);
}
}
}
TRACE("sat", s->display(tout););
algorithm2();
if (!s->inconsistent()) {
params_ref p;
p.set_uint("sat.max_conflicts", 10000);
p.set_bool("sat.binspr", false);
s->updt_params(p);
s->check(0, nullptr);
}
if (s->inconsistent()) {
s->set_conflict();
}
else {
s->pop_to_base_level();
for (unsigned i = m_solver.init_trail_size(); i < s->init_trail_size(); ++i) {
literal lit = s->trail_literal(i);
m_solver.assign(lit, s->get_justification(lit));
}
TRACE("sat", tout << "added " << (s->init_trail_size() - m_solver.init_trail_size()) << " units\n";);
}
}
/**
F, p |- ~u,
F, p, q |- ~v,
{ p, v } u C in F
{ q, u } u D in F
Then use { p, ~u, q, ~v } as alpha, L = { p, q }
for u in ~consequences of p:
for (u u D) in use(u):
for q in D, unassigned:
for v in ~consequences(q) | ({p, v} u C) in use(v):
check_spr(p, q, u, v)
*/
void binspr::algorithm2() {
mk_masks();
unsigned num_lits = 2 * s->num_vars();
for (unsigned l_idx = 0; l_idx < num_lits && !s->inconsistent(); ++l_idx) {
s->checkpoint();
literal p = to_literal(l_idx);
TRACE("sat", tout << "p " << p << " " << s->value(p) << "\n";);
if (is_used(p) && s->value(p) == l_undef) {
s->push();
s->assign_scoped(p);
unsigned sz_p = s->m_trail.size();
s->propagate(false);
if (s->inconsistent()) {
s->pop(1);
s->assign_unit(~p);
s->propagate(false);
TRACE("sat", s->display(tout << "unit\n"););
IF_VERBOSE(0, verbose_stream() << "unit " << (~p) << "\n");
continue;
}
for (unsigned i = sz_p; !s->inconsistent() && i < s->m_trail.size(); ++i) {
literal u = ~s->m_trail[i];
TRACE("sat", tout << "p " << p << " u " << u << "\n";);
for (clause* cp : m_use_list[u.index()]) {
for (literal q : *cp) {
if (s->inconsistent())
break;
if (s->value(q) != l_undef)
continue;
s->push();
s->assign_scoped(q);
unsigned sz_q = s->m_trail.size();
s->propagate(false);
if (s->inconsistent()) {
// learn ~p or ~q
s->pop(1);
block_binary(p, q, true);
s->propagate(false);
continue;
}
bool found = false;
for (unsigned j = sz_q; !found && j < s->m_trail.size(); ++j) {
literal v = ~s->m_trail[j];
for (clause* cp2 : m_use_list[v.index()]) {
if (cp2->contains(p)) {
if (check_spr(p, q, u, v)) {
found = true;
break;
}
}
}
}
s->pop(1);
if (found) {
block_binary(p, q, false);
s->propagate(false);
TRACE("sat", s->display(tout););
}
}
}
}
s->pop(1);
}
}
}
bool binspr::is_used(literal lit) const {
return !m_use_list[lit.index()].empty() || !s->get_wlist(~lit).empty();
}
bool binspr::check_spr(literal p, literal q, literal u, literal v) {
SASSERT(s->value(p) == l_true);
SASSERT(s->value(q) == l_true);
SASSERT(s->value(u) == l_false);
SASSERT(s->value(v) == l_false);
init_g(p, q, u, v);
literal lits[4] = { p, q, ~u, ~v };
for (unsigned i = 0; g_is_sat() && i < 4; ++i) {
binary_are_unit_implied(lits[i]);
clauses_are_unit_implied(lits[i]);
}
TRACE("sat", tout << p << " " << q << " " << u << " " << v << " " << g_is_sat() << "\n";);
return g_is_sat();
}
void binspr::binary_are_unit_implied(literal p) {
for (watched const& w : s->get_wlist(~p)) {
if (!g_is_sat()) {
break;
}
if (!w.is_binary_non_learned_clause()) {
continue;
}
clear_alpha();
VERIFY(touch(p));
literal lit = w.get_literal();
SASSERT(lit != p);
if (touch(lit)) {
add_touched();
continue;
}
bool inconsistent = (s->value(lit) == l_true);
if (s->value(lit) == l_undef) {
s->push();
s->assign_scoped(~lit);
s->propagate(false);
inconsistent = s->inconsistent();
s->pop(1);
}
if (!inconsistent) {
TRACE("sat", tout << "not implied: " << p << " " << lit << "\n";);
m_state = 0;
add_touched();
}
}
}
void binspr::clauses_are_unit_implied(literal p) {
for (clause* cp : m_use_list[p.index()]) {
if (!g_is_sat()) break;
clause_is_unit_implied(*cp);
}
}
void binspr::clause_is_unit_implied(clause const& c) {
s->push();
clear_alpha();
for (literal lit : c) {
if (touch(lit)) {
continue;
}
else if (s->value(lit) == l_true) {
s->pop(1);
return;
}
else if (s->value(lit) != l_false) {
s->assign_scoped(~lit);
}
}
s->propagate(false);
bool inconsistent = s->inconsistent();
s->pop(1);
if (!inconsistent) {
add_touched();
}
}
void binspr::block_binary(literal lit1, literal lit2, bool learned) {
IF_VERBOSE(2, verbose_stream() << "SPR: " << learned << " " << ~lit1 << " " << ~lit2 << "\n");
TRACE("sat", tout << "SPR: " << learned << " " << ~lit1 << " " << ~lit2 << "\n";);
s->mk_clause(~lit1, ~lit2, learned ? sat::status::redundant() : sat::status::asserted());
++m_bin_clauses;
}
void binspr::g_add_unit(literal lit1, literal lit2) {
if (lit1.var() < lit2.var()) {
m_state &= 0x2;
}
else {
m_state &= 0x4;
}
}
void binspr::g_add_binary(literal lit1, literal lit2, bool flip2) {
bool flip1 = false;
if (lit1.var() > lit2.var()) { std::swap(flip1, flip2); }
m_state &= ((flip1?0x5:0xA) | (flip2?0x3:0xC));
}
// 0 -> 10
// 1 -> 01
// * -> 11
// 00 -> 1000
// 10 -> 0100
// 01 -> 0010
// 11 -> 0001
// *1 -> 00110011
// *0 -> 11001100
// 0* -> 10101010
// 1* -> 01010101
// **1 -> 00001111
// **0 -> 11110000
/**
\brief create masks (lsb is left)
i = 0: 1010101010101010
i = 1: 1100110011001100
i = 2: 1111000011110000
*/
unsigned binspr::mk_mask(unsigned i) {
// variable number i is false.
unsigned mask0 = (1 << (1 << i)) - 1; // 2^i bits of ones
unsigned pos = 1 << (i+1); // how many bits in mask
unsigned mask = mask0;
while (pos < 32) {
mask |= (mask0 << pos);
pos += 1 << (i + 1);
}
return mask;
}
void binspr::mk_masks() {
for (unsigned i = 0; i < max_lits; ++i) {
m_false[i] = mk_mask(i);
m_true[i] = m_false[i] << (1 << i);
}
}
/**
\brief create Boolean function table
corresponding to disjunction of literals
*/
void binspr::add_touched() {
unsigned mask = 0;
for (unsigned i = 0; i < 4; ++i) {
switch (m_vals[i]) {
case l_true:
mask |= m_true[i];
break;
case l_false:
mask |= m_false[i];
break;
default:
break;
}
}
m_state &= mask;
TRACE("sat",
{
bool_var vars[4];
vars[0] = m_p; vars[1] = m_q; vars[2] = m_u; vars[3] = m_v;
tout << "touched: ";
for (unsigned i = 0; i < 4; ++i) {
switch (m_vals[i]) {
case l_true:
tout << literal(vars[i], false) << " ";
break;
case l_false:
tout << literal(vars[i], true) << " ";
break;
default:
break;
}
}
display_mask(tout << " ", m_state);
});
}
void binspr::init_g(literal p, literal q, literal u, literal v) {
m_p = p.var();
m_q = q.var();
m_u = u.var();
m_v = v.var();
m_state = ~0;
clear_alpha();
VERIFY(touch(~p));
VERIFY(touch(~q));
add_touched();
}
void binspr::clear_alpha() {
m_vals[0] = m_vals[1] = m_vals[2] = m_vals[3] = l_undef;
}
bool binspr::touch(literal p) {
bool_var v = p.var();
if (v == m_p) m_vals[0] = to_lbool(!p.sign());
else if (v == m_q) m_vals[1] = to_lbool(!p.sign());
else if (v == m_u) m_vals[2] = to_lbool(!p.sign());
else if (v == m_v) m_vals[3] = to_lbool(!p.sign());
else return false;
return true;
}
std::ostream& binspr::display_mask(std::ostream& out, unsigned mask) const {
for (unsigned i = 0; i < 4; ++i) {
out << m_vals[i] << " ";
}
out << " - ";
for (unsigned i = 0; i < 32; ++i) {
out << (0 != (mask & (1 << i)) ? 1 : 0);
}
return out << "\n";
}
}