3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-09-04 00:58:07 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2023-02-01 16:28:57 +01:00
commit 20b5455d08
669 changed files with 26145 additions and 20652 deletions

View file

@ -30,6 +30,7 @@ z3_add_component(sat
sat_parallel.cpp
sat_prob.cpp
sat_probing.cpp
sat_proof_trim.cpp
sat_scc.cpp
sat_simplifier.cpp
sat_solver.cpp

View file

@ -64,7 +64,6 @@ namespace sat {
}
TRACE("cleanup_bug", tout << "keeping: " << ~to_literal(l_idx) << " " << it2->get_literal() << "\n";);
break;
case watched::TERNARY:
case watched::CLAUSE:
// skip
break;

View file

@ -46,7 +46,7 @@ namespace sat {
else if (s == symbol("static"))
m_restart = RS_STATIC;
else
throw sat_param_exception("invalid restart strategy");
throw sat_param_exception("invalid restart strategy. Use ema (default), luby, geometric, static");
m_fast_glue_avg = p.restart_emafastglue();
m_slow_glue_avg = p.restart_emaslowglue();
@ -197,8 +197,17 @@ namespace sat {
m_drat_check_unsat = p.drat_check_unsat();
m_drat_check_sat = p.drat_check_sat();
m_drat_file = p.drat_file();
m_smt_proof = p.smt_proof();
m_drat = !p.drat_disable() && (sp.lemmas2console() || m_drat_check_unsat || m_drat_file.is_non_empty_string() || m_smt_proof.is_non_empty_string() || m_drat_check_sat) && p.threads() == 1;
m_smt_proof_check = p.smt_proof_check();
m_smt_proof_check_rup = p.smt_proof_check_rup();
m_drat_disable = p.drat_disable();
m_drat =
!m_drat_disable && p.threads() == 1 &&
(sp.lemmas2console() ||
m_drat_check_unsat ||
m_drat_file.is_non_empty_string() ||
sp.proof_log().is_non_empty_string() ||
m_smt_proof_check ||
m_drat_check_sat);
m_drat_binary = p.drat_binary();
m_drat_activity = p.drat_activity();
m_dyn_sub_res = p.dyn_sub_res();

View file

@ -175,9 +175,11 @@ namespace sat {
// drat proofs
bool m_drat;
bool m_drat_disable;
bool m_drat_binary;
symbol m_drat_file;
symbol m_smt_proof;
bool m_smt_proof_check;
bool m_smt_proof_check_rup;
bool m_drat_check_unsat;
bool m_drat_check_sat;
bool m_drat_activity;

View file

@ -57,17 +57,8 @@ namespace sat {
}
std::ostream& drat::pp(std::ostream& out, status st) const {
if (st.is_redundant())
out << "l";
else if (st.is_deleted())
if (st.is_deleted())
out << "d";
else if (st.is_asserted())
out << "a";
else if (st.is_input())
out << "i";
if (!st.is_sat())
out << " " << m_theory[st.get_th()];
return out;
}
@ -102,11 +93,6 @@ namespace sat {
}
}
if (!st.is_sat()) {
for (char ch : m_theory[st.get_th()])
buffer[len++] = ch;
buffer[len++] = ' ';
}
for (unsigned i = 0; i < n; ++i) {
literal lit = c[i];
unsigned v = lit.var();
@ -386,7 +372,7 @@ namespace sat {
}
}
CTRACE("sat_drat", num_true == 0 && num_undef == 1, display(tout););
SASSERT(num_true != 0 || num_undef != 1);
VERIFY(num_true != 0 || num_undef != 1);
}
}
}
@ -459,8 +445,6 @@ namespace sat {
return false;
case justification::BINARY:
return contains(c, j.get_literal());
case justification::TERNARY:
return contains(c, j.get_literal1(), j.get_literal2());
case justification::CLAUSE:
return contains(s.get_clause(j));
default:
@ -656,7 +640,6 @@ namespace sat {
if (m_out) dump(1, &l, st);
if (m_bout) bdump(1, &l, st);
if (m_check) append(l, st);
TRACE("sat", tout << "add " << m_clause_eh << "\n");
if (m_clause_eh) m_clause_eh->on_clause(1, &l, st);
}
void drat::add(literal l1, literal l2, status st) {

View file

@ -17,39 +17,6 @@ Notes:
For DIMACS input it produces DRAT proofs.
For SMT extensions are as follows:
Input assertion:
i <literal>* 0
Assertion (true modulo a theory):
a [<theory-id>] <literal>* 0
The if no theory id is given, the assertion is a tautology
modulo Tseitin converison. Theory ids track whether the
tautology is modulo a theory.
Assertions are irredundant.
Bridge from ast-node to boolean variable:
b <bool-var-id> <ast-node-id> 0
Definition of an expression (ast-node):
e <ast-node-id> <name> <ast-node-id>* 0
Redundant clause (theory lemma if theory id is given)
[r [<theory-id>]] <literal>* 0
Declaration of an auxiliary function:
f <smtlib2-function-declaration> 0
Garbage collection of a Boolean variable:
g <bool-var-id> 0
Available theories are:
- euf The theory lemma should be a consequence of congruence closure.
- ba TBD (need to also log cardinality and pb constraints)
Life times of theory lemmas is TBD. When they are used for conflict resolution
they are only used for the next lemma.
--*/
#pragma once
@ -89,7 +56,6 @@ namespace sat {
svector<std::pair<literal, clause*>> m_units;
vector<watch> m_watches;
svector<lbool> m_assignment;
vector<std::string> m_theory;
bool m_inconsistent = false;
bool m_check_unsat = false;
bool m_check_sat = false;
@ -135,7 +101,6 @@ namespace sat {
void updt_config();
void add_theory(int id, symbol const& s) { m_theory.setx(id, s.str(), std::string()); }
void add();
void add(literal l, bool learned);
void add(literal l1, literal l2, status st);

View file

@ -72,7 +72,7 @@ namespace sat {
extension(symbol const& name, int id): m_id(id), m_name(name) { }
virtual ~extension() = default;
int get_id() const { return m_id; }
void set_solver(solver* s) { m_solver = s; }
virtual void set_solver(solver* s) { m_solver = s; }
solver& s() { return *m_solver; }
solver const& s() const { return *m_solver; }
symbol const& name() const { return m_name; }
@ -133,6 +133,8 @@ namespace sat {
return false;
}
virtual bool is_pb() { return false; }
virtual std::string reason_unknown() { return "unknown"; }
};
};

View file

@ -20,8 +20,6 @@ Revision History:
#include "sat/sat_solver.h"
#define ENABLE_TERNARY true
namespace sat {
// -----------------------
@ -180,29 +178,9 @@ namespace sat {
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << (sz - new_sz) << ")\n";);
}
bool solver::can_delete3(literal l1, literal l2, literal l3) const {
if (value(l1) == l_true &&
value(l2) == l_false &&
value(l3) == l_false) {
justification const& j = m_justification[l1.var()];
if (j.is_ternary_clause()) {
watched w1(l2, l3);
watched w2(j.get_literal1(), j.get_literal2());
return w1 != w2;
}
}
return true;
}
bool solver::can_delete(clause const & c) const {
if (c.on_reinit_stack())
return false;
if (ENABLE_TERNARY && c.size() == 3) {
return
can_delete3(c[0],c[1],c[2]) &&
can_delete3(c[1],c[0],c[2]) &&
can_delete3(c[2],c[0],c[1]);
}
literal l0 = c[0];
if (value(l0) != l_true)
return true;

View file

@ -26,11 +26,6 @@ namespace sat {
integrity_checker::integrity_checker(solver const & _s):
s(_s) {
}
// for ternary clauses
static bool contains_watched(watch_list const & wlist, literal l1, literal l2) {
return wlist.contains(watched(l1, l2));
}
// for nary clauses
static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) {
@ -63,16 +58,7 @@ namespace sat {
if (c.frozen())
return true;
if (c.size() == 3) {
CTRACE("sat_ter_watch_bug", !contains_watched(s.get_wlist(~c[0]), c[1], c[2]), tout << c << "\n";
tout << "watch_list:\n";
s.display_watch_list(tout, s.get_wlist(~c[0]));
tout << "\n";);
VERIFY(contains_watched(s.get_wlist(~c[0]), c[1], c[2]));
VERIFY(contains_watched(s.get_wlist(~c[1]), c[0], c[2]));
VERIFY(contains_watched(s.get_wlist(~c[2]), c[0], c[1]));
}
else {
{
if (s.value(c[0]) == l_false || s.value(c[1]) == l_false) {
bool on_prop_stack = false;
for (unsigned i = s.m_qhead; i < s.m_trail.size(); i++) {
@ -119,7 +105,7 @@ namespace sat {
if (c.frozen())
num_frozen++;
}
SASSERT(num_frozen == s.m_num_frozen);
VERIFY(num_frozen == s.m_num_frozen);
return check_clauses(s.begin_learned(), s.end_learned());
}
@ -169,11 +155,6 @@ namespace sat {
tout << "\n";);
VERIFY(find_binary_watch(s.get_wlist(~(w.get_literal())), l));
break;
case watched::TERNARY:
VERIFY(!s.was_eliminated(w.get_literal1().var()));
VERIFY(!s.was_eliminated(w.get_literal2().var()));
VERIFY(w.get_literal1().index() < w.get_literal2().index());
break;
case watched::CLAUSE:
VERIFY(!s.get_clause(w.get_clause_offset()).was_removed());
break;

View file

@ -22,7 +22,7 @@ namespace sat {
class justification {
public:
enum kind { NONE = 0, BINARY = 1, TERNARY = 2, CLAUSE = 3, EXT_JUSTIFICATION = 4};
enum kind { NONE = 0, BINARY = 1, CLAUSE = 2, EXT_JUSTIFICATION = 3};
private:
unsigned m_level;
size_t m_val1;
@ -32,7 +32,7 @@ namespace sat {
public:
justification(unsigned lvl):m_level(lvl), m_val1(0), m_val2(NONE) {}
explicit justification(unsigned lvl, literal l):m_level(lvl), m_val1(l.to_uint()), m_val2(BINARY) {}
justification(unsigned lvl, literal l1, literal l2):m_level(lvl), m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {}
explicit justification(unsigned lvl, clause_offset cls_off):m_level(lvl), m_val1(cls_off), m_val2(CLAUSE) {}
static justification mk_ext_justification(unsigned lvl, ext_justification_idx idx) { return justification(lvl, idx, EXT_JUSTIFICATION); }
@ -45,10 +45,6 @@ namespace sat {
bool is_binary_clause() const { return m_val2 == BINARY; }
literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(val1()); }
bool is_ternary_clause() const { return get_kind() == TERNARY; }
literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(val1()); }
literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 3); }
bool is_clause() const { return m_val2 == CLAUSE; }
clause_offset get_clause_offset() const { return m_val1; }
@ -65,9 +61,6 @@ namespace sat {
case justification::BINARY:
out << "binary " << j.get_literal();
break;
case justification::TERNARY:
out << "ternary " << j.get_literal1() << " " << j.get_literal2();
break;
case justification::CLAUSE:
out << "clause";
break;

View file

@ -580,7 +580,6 @@ namespace sat {
bool_var v = null_bool_var;
unsigned num_unsat = m_unsat_stack.size();
constraint const& c = m_constraints[m_unsat_stack[m_rand() % num_unsat]];
unsigned reflipped = 0;
bool is_core = m_unsat_stack.size() <= 10;
if (m_rand() % 10000 <= m_noise) {
// take this branch with 98% probability.
@ -684,7 +683,6 @@ namespace sat {
}
if (false && is_core && c.m_k < constraint_value(c)) {
++reflipped;
goto reflip;
}
}

View file

@ -1001,6 +1001,7 @@ namespace sat {
m_inconsistent = false;
m_qhead = 0;
m_bstamp_id = 0;
m_istamp_id = 0;
for (unsigned i = 0; i < m_num_vars; ++i) {
init_var(i);

View file

@ -124,8 +124,8 @@ namespace sat {
}
bool lut_finder::extract_lut(literal l1, literal l2) {
SASSERT(s.is_visited(l1.var()));
SASSERT(s.is_visited(l2.var()));
SASSERT(s.m_visited.is_visited(l1.var()));
SASSERT(s.m_visited.is_visited(l2.var()));
m_missing.reset();
unsigned mask = 0;
for (unsigned i = 0; i < m_vars.size(); ++i) {

View file

@ -167,6 +167,7 @@ namespace sat {
// end of clause
if (!sat) {
TRACE("sat_model_bug", tout << "failed eliminated: " << mk_lits_pp(static_cast<unsigned>(it - itbegin), itbegin) << "\n";);
(void)itbegin;
ok = false;
}
sat = false;

View file

@ -47,7 +47,9 @@ def_module_params('sat',
('threads', UINT, 1, 'number of parallel threads to use'),
('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'),
('drat.disable', BOOL, False, 'override anything that enables DRAT'),
('smt.proof', SYMBOL, '', 'add SMT proof to file'),
('smt', BOOL, False, 'use the SAT solver based incremental SMT core'),
('smt.proof.check', BOOL, False, 'check SMT proof while it is created'),
('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'),
('drat.file', SYMBOL, '', 'file to dump DRAT proofs'),
('drat.binary', BOOL, False, 'use Binary DRAT output format'),
('drat.check_unsat', BOOL, False, 'build up internal proof and check'),
@ -72,11 +74,11 @@ def_module_params('sat',
('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'),
('local_search_dbg_flips', BOOL, False, 'write debug information for number of flips'),
('binspr', BOOL, False, 'enable SPR inferences of binary propagation redundant clauses. This inprocessing step eliminates models'),
('anf', BOOL, False, 'enable ANF based simplification in-processing'),
('anf.delay', UINT, 2, 'delay ANF simplification by in-processing round'),
('anf', BOOL, False, 'enable ANF based simplification in-processing'),
('anf.delay', UINT, 2, 'delay ANF simplification by in-processing round'),
('anf.exlin', BOOL, False, 'enable extended linear simplification'),
('cut', BOOL, False, 'enable AIG based simplification in-processing'),
('cut.delay', UINT, 2, 'delay cut simplification by in-processing round'),
('cut', BOOL, False, 'enable AIG based simplification in-processing'),
('cut.delay', UINT, 2, 'delay cut simplification by in-processing round'),
('cut.aig', BOOL, False, 'extract aigs (and ites) from cluases for cut simplification'),
('cut.lut', BOOL, False, 'extract luts from clauses for cut simplification'),
('cut.xor', BOOL, False, 'extract xors from clauses for cut simplification'),

357
src/sat/sat_proof_trim.cpp Normal file
View file

@ -0,0 +1,357 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
sat_proof_trim.cpp
Abstract:
proof replay and trim
Author:
Nikolaj Bjorner 2023-10-04
Notes:
--*/
#include "sat/sat_proof_trim.h"
namespace sat {
/**
Pseudo-code from Gurfinkel, Vizel, FMCAD 2014
Input: trail (a0,d0), ..., (an,dn) = ({},bot)
Output: reduced trail - result
*/
unsigned_vector proof_trim::trim() {
unsigned_vector result;
m_core_literals.reset();
m_core_literals.insert(literal_vector());
m_propagated.resize(num_vars(), false);
for (unsigned i = m_trail.size(); i-- > 0; ) {
auto const& [id, cl, clp, is_add, is_initial] = m_trail[i];
if (!is_add) {
revive(cl, clp);
continue;
}
IF_VERBOSE(10, s.display(verbose_stream()));
prune_trail(cl, clp);
IF_VERBOSE(10, verbose_stream() << cl << " " << in_core(cl, clp) << ": "; for (auto const& c : m_core_literals) verbose_stream() << "{" << c << "} ");
IF_VERBOSE(10, s.display(verbose_stream() << "\n"));
del(cl, clp);
if (!in_core(cl, clp))
continue;
result.push_back(id);
if (is_initial)
continue;
conflict_analysis_core(cl, clp);
}
result.reverse();
return result;
}
void proof_trim::del(literal_vector const& cl, clause* cp) {
if (cp)
s.detach_clause(*cp);
else
del(cl);
}
bool proof_trim::match_clause(literal_vector const& cl, literal l1, literal l2) const {
return cl.size() == 2 && ((l1 == cl[0] && l2 == cl[1]) || (l1 == cl[1] && l2 == cl[0]));
}
bool proof_trim::match_clause(literal_vector const& cl, literal l1, literal l2, literal l3) const {
return cl.size() == 3 &&
((l1 == cl[0] && l2 == cl[1] && l3 == cl[2]) ||
(l1 == cl[0] && l2 == cl[2] && l3 == cl[1]) ||
(l1 == cl[1] && l2 == cl[0] && l3 == cl[2]) ||
(l1 == cl[1] && l2 == cl[2] && l3 == cl[0]) ||
(l1 == cl[2] && l2 == cl[1] && l3 == cl[0]) ||
(l1 == cl[2] && l2 == cl[0] && l3 == cl[1]));
}
/**
* cl is on the trail if there is some literal l that is implied by cl
* Remove all clauses after cl that are in the cone of influence of cl.
* The coi is defined inductively: C is in coi of cl if it contains ~l
* or it contains ~l' where l' is implied by a clause in the coi of cl.
* Possible optimization:
* - check if clause contains a literal that is on implied on the trail
* if it doesn't contain any such literal, bypass the trail adjustment.
*/
void proof_trim::prune_trail(literal_vector const& cl, clause* cp) {
m_in_clause.reset();
m_in_coi.reset();
if (cl.empty())
return;
for (literal lit : cl)
m_in_clause.insert(lit.index());
auto unassign_literal = [&](literal l) {
m_in_coi.insert((~l).index());
s.m_assignment[l.index()] = l_undef;
s.m_assignment[(~l).index()] = l_undef;
};
bool on_trail = false;
unsigned j = 0;
for (unsigned i = 0; i < s.trail_size(); ++i) {
literal l = s.trail_literal(i);
if (m_in_clause.contains(l.index())) {
SASSERT(!on_trail);
on_trail = true;
unassign_literal(l);
continue;
}
if (!on_trail) {
s.m_trail[j++] = s.m_trail[i];
continue;
}
auto js = s.get_justification(l);
bool in_coi = false;
if (js.is_clause())
for (literal lit : s.get_clause(j))
in_coi |= m_in_coi.contains(lit.index());
else if (js.is_binary_clause())
in_coi = m_in_coi.contains(js.get_literal().index());
else
UNREACHABLE(); // approach does not work for external justifications
if (in_coi)
unassign_literal(l);
else
s.m_trail[j++] = s.m_trail[i];
}
s.m_trail.shrink(j);
s.m_inconsistent = false;
s.m_qhead = s.m_trail.size();
s.propagate(false);
}
/**
The current state is in conflict.
Chase justifications for conflict to extract clauses that are in coi of conflict.
Assume:
F | G, ~C |- []
Let T (trail) be the extension of G, ~C that derives the empty clause.
T := G, ~C, l1:j1, l2:j2, ..., lk:jk
The goal is to extract clauses in T that are used to derive C.
This is achieved by collecting all literals from j1, j2, ... jk
and the conflict clause that are at level below ~C and using the clauses that justify those literals.
Example:
C = c or d or e
G = a
F = { ~a or ~b, c or d or b, ... }
T = ~b : ~a or ~b, ~c: D ~d : D , ~e : D, b : c or d or b
where D is a decision marker (justification::NONE)
The conflict depends on the first two clauses in F.
All literals that are are used in clauses leading to the conflict are
queried for their explanation. Their explanation is added to the clauses.
*/
void proof_trim::conflict_analysis_core(literal_vector const& cl, clause* cp) {
IF_VERBOSE(3, verbose_stream() << "core " << cl << "\n");
unsigned trail_size0 = s.m_trail.size();
if (!cl.empty()) {
SASSERT(!s.inconsistent());
s.push();
unsigned lvl = s.scope_lvl();
for (auto lit : cl)
s.assign(~lit, justification(lvl));
trail_size0 = s.m_trail.size();
s.propagate(false);
if (!s.inconsistent()) {
s.m_qhead = 0;
s.propagate(false);
}
if (!s.inconsistent())
IF_VERBOSE(0, s.display(verbose_stream()));
for (unsigned i = trail_size0; i < s.m_trail.size(); ++i)
m_propagated[s.m_trail[i].var()] = true;
}
SASSERT(s.inconsistent());
IF_VERBOSE(3, verbose_stream() << s.m_not_l << " " << s.m_conflict << "\n");
if (s.m_not_l != null_literal) {
add_core(~s.m_not_l, s.m_conflict);
add_dependency(s.m_not_l);
}
add_dependency(s.m_conflict);
for (unsigned i = s.m_trail.size(); i-- > trail_size0; ) {
bool_var v = s.m_trail[i].var();
m_propagated[v] = false;
if (!s.is_marked(v))
continue;
add_core(v);
s.reset_mark(v);
add_dependency(s.get_justification(v));
}
if (!cl.empty())
s.pop(1);
}
void proof_trim::add_dependency(literal lit) {
bool_var v = lit.var();
if (m_propagated[v]) // literal was propagated after assuming ~C
s.mark(v);
else if (s.lvl(v) == 0) // literal depends on level 0, it is not assumed by ~C
// inefficient for repeated insertions ?
add_core(v);
}
void proof_trim::add_dependency(justification j) {
switch (j.get_kind()) {
case justification::BINARY:
add_dependency(j.get_literal());
break;
case justification::CLAUSE:
for (auto lit : s.get_clause(j))
if (s.value(lit) == l_false)
add_dependency(lit);
break;
case justification::EXT_JUSTIFICATION:
UNREACHABLE();
break;
default:
break;
}
}
void proof_trim::add_core(bool_var v) {
auto j = s.get_justification(v);
literal lit = literal(v, s.value(v) == l_false);
add_core(lit, j);
}
void proof_trim::add_core(literal l, justification j) {
m_clause.reset();
switch (j.get_kind()) {
case justification::NONE:
m_clause.push_back(l);
break;
case justification::BINARY:
m_clause.push_back(l);
m_clause.push_back(j.get_literal());
break;
case justification::CLAUSE:
s.get_clause(j).mark_used();
IF_VERBOSE(3, verbose_stream() << "add core " << s.get_clause(j) << "\n");
return;
default:
UNREACHABLE();
break;
}
std::sort(m_clause.begin(), m_clause.end());
IF_VERBOSE(3, verbose_stream() << "add core " << m_clause << "\n");
m_core_literals.insert(m_clause);
if (s.lvl(l) == 0) {
m_clause.reset();
m_clause.push_back(l);
m_core_literals.insert(m_clause);
}
}
bool proof_trim::in_core(literal_vector const& cl, clause* cp) const {
if (cp)
return cp->was_used();
else
return m_core_literals.contains(cl);
}
void proof_trim::revive(literal_vector const& cl, clause* cp) {
if (cp)
s.attach_clause(*cp);
else
s.mk_clause(cl, status::redundant());
}
clause* proof_trim::del(literal_vector const& cl) {
clause* cp = nullptr;
IF_VERBOSE(3, verbose_stream() << "del: " << cl << "\n");
if (cl.size() == 2) {
s.detach_bin_clause(cl[0], cl[1], true);
return cp;
}
auto* e = m_clauses.find_core(cl);
if (!e)
return cp;
auto& v = e->get_data().m_value;
if (!v.empty()) {
cp = v.back();
IF_VERBOSE(3, verbose_stream() << "del: " << *cp << "\n");
s.detach_clause(*cp);
v.pop_back();
}
return cp;
}
void proof_trim::save(literal_vector const& lits, clause* cl) {
if (!cl)
return;
IF_VERBOSE(3, verbose_stream() << "add: " << *cl << "\n");
auto& v = m_clauses.insert_if_not_there(lits, clause_vector());
v.push_back(cl);
}
proof_trim::proof_trim(params_ref const& p, reslimit& lim):
s(p, lim) {
s.set_trim();
}
void proof_trim::assume(unsigned id, bool is_initial) {
std::sort(m_clause.begin(), m_clause.end());
if (unit_or_binary_occurs())
return;
IF_VERBOSE(3, verbose_stream() << (is_initial?"assume ":"rup ") << m_clause << "\n");
auto* cl = s.mk_clause(m_clause, status::redundant());
m_trail.push_back({ id, m_clause, cl, true, is_initial });
s.propagate(false);
save(m_clause, cl);
}
/**
* Unit clauses (and binary clause) do not have multi-set semantics in the solver.
* So they should only be represented once.
*/
bool proof_trim::unit_or_binary_occurs() {
if (m_clause.size() == 1) {
literal lit = m_clause[0];
if (m_units.contains(lit.index()))
return true;
m_units.insert(lit.index());
}
// todo: binary?
return false;
}
void proof_trim::del() {
std::sort(m_clause.begin(), m_clause.end());
clause* cp = del(m_clause);
m_trail.push_back({ 0, m_clause, cp, false, true });
}
void proof_trim::infer(unsigned id) {
assume(id, false);
}
}

91
src/sat/sat_proof_trim.h Normal file
View file

@ -0,0 +1,91 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
sat_trim.h
Abstract:
proof replay and trim
Author:
Nikolaj Bjorner 2023-10-04
Notes:
--*/
#pragma once
#include "util/params.h"
#include "util/statistics.h"
#include "sat/sat_clause.h"
#include "sat/sat_types.h"
#include "sat/sat_solver.h"
namespace sat {
class proof_trim {
solver s;
literal_vector m_clause;
uint_set m_in_clause;
uint_set m_in_coi;
vector<std::tuple<unsigned, literal_vector, clause*, bool, bool>> m_trail;
struct hash {
unsigned operator()(literal_vector const& v) const {
return string_hash((char const*)v.begin(), v.size()*sizeof(literal), 3);
}
};
struct eq {
bool operator()(literal_vector const& a, literal_vector const& b) const {
return a == b;
}
};
map<literal_vector, clause_vector, hash, eq> m_clauses;
hashtable<literal_vector, hash, eq> m_core_literals;
bool_vector m_propagated;
void del(literal_vector const& cl, clause* cp);
bool match_clause(literal_vector const& cl, literal l1, literal l2) const;
bool match_clause(literal_vector const& cl, literal l1, literal l2, literal l3) const;
void prune_trail(literal_vector const& cl, clause* cp);
void conflict_analysis_core(literal_vector const& cl, clause* cp);
void add_dependency(literal lit);
void add_dependency(justification j);
void add_core(bool_var v);
void add_core(literal l, justification j);
bool in_core(literal_vector const& cl, clause* cp) const;
void revive(literal_vector const& cl, clause* cp);
clause* del(literal_vector const& cl);
void save(literal_vector const& lits, clause* cl);
uint_set m_units;
bool unit_or_binary_occurs();
public:
proof_trim(params_ref const& p, reslimit& lim);
bool_var mk_var() { return s.mk_var(true, true); }
void init_clause() { m_clause.reset(); }
void add_literal(bool_var v, bool sign) { m_clause.push_back(literal(v, sign)); }
unsigned num_vars() { return s.num_vars(); }
void assume(unsigned id, bool is_initial = true);
void del();
void infer(unsigned id);
void updt_params(params_ref const& p) { s.updt_params(p); }
unsigned_vector trim();
};
}

View file

@ -229,6 +229,7 @@ namespace sat {
}
}
unsigned count = 0;
do {
if (m_subsumption)
subsume();
@ -240,8 +241,9 @@ namespace sat {
return;
if (!m_subsumption || m_sub_counter < 0)
break;
++count;
}
while (!m_sub_todo.empty());
while (!m_sub_todo.empty() && count < 20);
bool vars_eliminated = m_num_elim_vars > m_old_num_elim_vars;
if (m_need_cleanup || vars_eliminated) {
@ -269,7 +271,6 @@ namespace sat {
watch_list::iterator end2 = wlist.end();
for (; it2 != end2; ++it2) {
switch (it2->get_kind()) {
case watched::TERNARY:
case watched::CLAUSE:
// consume
break;
@ -287,16 +288,13 @@ namespace sat {
clause_vector::iterator it = cs.begin();
clause_vector::iterator it2 = it;
clause_vector::iterator end = cs.end();
unsigned nm = 0;
for (; it != end; ++it) {
clause & c = *(*it);
if (learned && !c.is_learned()) {
s.m_clauses.push_back(&c);
++nm;
}
else if (!learned && c.is_learned()) {
s.m_learned.push_back(&c);
++nm;
}
else {
*it2 = *it;
@ -2000,7 +1998,7 @@ namespace sat {
m_elim_counter -= num_pos * num_neg + before_lits;
for (auto & c1 : m_pos_cls) {
if (c1.was_removed())
if (c1.was_removed() && !c1.contains(pos_l))
continue;
for (auto & c2 : m_neg_cls) {
m_new_cls.reset();

View file

@ -37,7 +37,6 @@ Revision History:
# include <xmmintrin.h>
#endif
#define ENABLE_TERNARY true
namespace sat {
@ -284,6 +283,7 @@ namespace sat {
m_model_is_current = false;
m_stats.m_mk_var++;
bool_var v = m_justification.size();
if (!m_free_vars.empty()) {
v = m_free_vars.back();
m_free_vars.pop_back();
@ -303,7 +303,7 @@ namespace sat {
m_external.push_back(ext);
m_var_scope.push_back(scope_lvl());
m_touched.push_back(0);
m_activity.push_back(0);
m_activity.push_back(0);
m_mark.push_back(false);
m_lit_mark.push_back(false);
m_lit_mark.push_back(false);
@ -374,15 +374,15 @@ namespace sat {
}
void solver::del_clause(clause& c) {
if (!c.is_learned()) {
if (!c.is_learned())
m_stats.m_non_learned_generation++;
}
if (c.frozen()) {
if (c.frozen())
--m_num_frozen;
}
if (!c.was_removed() && m_config.m_drat && !m_drat.is_cleaned(c)) {
if (!c.was_removed() && m_config.m_drat && !m_drat.is_cleaned(c))
m_drat.del(c);
}
dealloc_clause(&c);
if (m_searching)
m_stats.m_del_clause++;
@ -403,8 +403,8 @@ namespace sat {
extension::scoped_drating _sd(*m_ext.get());
if (j.get_kind() == justification::EXT_JUSTIFICATION)
fill_ext_antecedents(lit, j, false);
TRACE("sat", tout << "drat-unit\n");
m_drat.add(lit, m_searching);
else
m_drat.add(lit, m_searching);
}
void solver::drat_log_clause(unsigned num_lits, literal const* lits, sat::status st) {
@ -417,7 +417,7 @@ namespace sat {
bool logged = false;
if (!redundant || !st.is_sat()) {
unsigned old_sz = num_lits;
bool keep = simplify_clause(num_lits, lits);
bool keep = m_trim || simplify_clause(num_lits, lits);
TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";);
if (!keep) {
return nullptr; // clause is equivalent to true.
@ -433,25 +433,23 @@ namespace sat {
m_mc.add_clause(num_lits, lits);
}
switch (num_lits) {
case 0:
set_conflict();
return nullptr;
case 1:
if (!logged && m_config.m_drat && (!st.is_sat() || st.is_input()))
if (!logged && m_config.m_drat)
drat_log_clause(num_lits, lits, st);
assign_unit(lits[0]);
{
flet<bool> _disable_drat(m_config.m_drat, false);
assign(lits[0], justification(0));
}
return nullptr;
case 2:
mk_bin_clause(lits[0], lits[1], st);
if (redundant && m_par)
m_par->share_clause(*this, lits[0], lits[1]);
return nullptr;
case 3:
if (ENABLE_TERNARY)
return mk_ter_clause(lits, st);
Z3_fallthrough;
default:
return mk_nary_clause(num_lits, lits, st);
}
@ -461,17 +459,20 @@ namespace sat {
bool redundant = st.is_redundant();
m_touched[l1.var()] = m_touch_index;
m_touched[l2.var()] = m_touch_index;
if (m_config.m_drat)
m_drat.add(l1, l2, st);
if (redundant && find_binary_watch(get_wlist(~l1), ~l2) && value(l1) == l_undef) {
if (redundant && !m_trim && find_binary_watch(get_wlist(~l1), ~l2) && value(l1) == l_undef) {
assign_unit(l1);
return;
}
if (redundant && find_binary_watch(get_wlist(~l2), ~l1) && value(l2) == l_undef) {
if (redundant && !m_trim && find_binary_watch(get_wlist(~l2), ~l1) && value(l2) == l_undef) {
assign_unit(l2);
return;
}
watched* w0 = redundant ? find_binary_watch(get_wlist(~l1), l2) : nullptr;
if (w0) {
if (w0 && !m_trim) {
TRACE("sat", tout << "found binary " << l1 << " " << l2 << "\n";);
if (w0->is_learned() && !redundant) {
w0->set_learned(false);
@ -485,12 +486,12 @@ namespace sat {
push_reinit_stack(l1, l2);
return;
}
if (m_config.m_drat)
m_drat.add(l1, l2, st);
if (propagate_bin_clause(l1, l2)) {
if (at_base_lvl())
if (!at_base_lvl())
push_reinit_stack(l1, l2);
else if (!m_trim)
return;
push_reinit_stack(l1, l2);
}
else if (has_variables_to_reinit(l1, l2))
push_reinit_stack(l1, l2);
@ -542,73 +543,22 @@ namespace sat {
m_clauses_to_reinit.push_back(clause_wrapper(l1, l2));
}
clause * solver::mk_ter_clause(literal * lits, sat::status st) {
VERIFY(ENABLE_TERNARY);
m_stats.m_mk_ter_clause++;
clause * r = alloc_clause(3, lits, st.is_redundant());
bool reinit = attach_ter_clause(*r, st);
if (reinit || has_variables_to_reinit(*r)) push_reinit_stack(*r);
if (st.is_redundant())
m_learned.push_back(r);
else
m_clauses.push_back(r);
for (literal l : *r) {
m_touched[l.var()] = m_touch_index;
}
return r;
}
bool solver::attach_ter_clause(clause & c, sat::status st) {
VERIFY(ENABLE_TERNARY);
bool reinit = false;
if (m_config.m_drat) m_drat.add(c, st);
TRACE("sat_verbose", tout << c << "\n";);
SASSERT(!c.was_removed());
m_watches[(~c[0]).index()].push_back(watched(c[1], c[2]));
m_watches[(~c[1]).index()].push_back(watched(c[0], c[2]));
m_watches[(~c[2]).index()].push_back(watched(c[0], c[1]));
if (!at_base_lvl())
reinit = propagate_ter_clause(c);
return reinit;
}
bool solver::propagate_ter_clause(clause& c) {
bool reinit = false;
if (value(c[1]) == l_false && value(c[2]) == l_false) {
m_stats.m_ter_propagate++;
assign(c[0], justification(std::max(lvl(c[1]), lvl(c[2])), c[1], c[2]));
reinit = !c.is_learned();
}
else if (value(c[0]) == l_false && value(c[2]) == l_false) {
m_stats.m_ter_propagate++;
assign(c[1], justification(std::max(lvl(c[0]), lvl(c[2])), c[0], c[2]));
reinit = !c.is_learned();
}
else if (value(c[0]) == l_false && value(c[1]) == l_false) {
m_stats.m_ter_propagate++;
assign(c[2], justification(std::max(lvl(c[0]), lvl(c[1])), c[0], c[1]));
reinit = !c.is_learned();
}
return reinit;
}
clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, sat::status st) {
m_stats.m_mk_clause++;
clause * r = alloc_clause(num_lits, lits, st.is_redundant());
SASSERT(!st.is_redundant() || r->is_learned());
bool reinit = attach_nary_clause(*r, st.is_sat() && st.is_redundant());
if (reinit || has_variables_to_reinit(*r)) push_reinit_stack(*r);
if (st.is_redundant()) {
if (reinit || has_variables_to_reinit(*r))
push_reinit_stack(*r);
if (st.is_redundant())
m_learned.push_back(r);
}
else {
else
m_clauses.push_back(r);
}
if (m_config.m_drat)
m_drat.add(*r, st);
for (literal l : *r) {
for (literal l : *r)
m_touched[l.var()] = m_touch_index;
}
return r;
}
@ -659,11 +609,7 @@ namespace sat {
void solver::attach_clause(clause & c, bool & reinit) {
SASSERT(c.size() > 2);
reinit = false;
if (ENABLE_TERNARY && c.size() == 3)
reinit = attach_ter_clause(c, c.is_learned() ? sat::status::redundant() : sat::status::asserted());
else
reinit = attach_nary_clause(c, c.is_learned() && !c.on_reinit_stack());
reinit = attach_nary_clause(c, c.is_learned() && !c.on_reinit_stack());
}
void solver::set_learned(clause& c, bool redundant) {
@ -910,11 +856,8 @@ namespace sat {
if (m_config.m_drat) m_drat.del(l1, l2);
}
void solver::detach_clause(clause & c) {
if (ENABLE_TERNARY && c.size() == 3)
detach_ter_clause(c);
else
detach_nary_clause(c);
void solver::detach_clause(clause& c) {
detach_nary_clause(c);
}
void solver::detach_nary_clause(clause & c) {
@ -923,12 +866,6 @@ namespace sat {
erase_clause_watch(get_wlist(~c[1]), cls_off);
}
void solver::detach_ter_clause(clause & c) {
erase_ternary_watch(get_wlist(~c[0]), c[1], c[2]);
erase_ternary_watch(get_wlist(~c[1]), c[0], c[2]);
erase_ternary_watch(get_wlist(~c[2]), c[0], c[1]);
}
// -----------------------
//
// Basic
@ -951,7 +888,8 @@ namespace sat {
if (j.level() == 0) {
if (m_config.m_drat)
drat_log_unit(l, j);
j = justification(0); // erase justification for level 0
if (!m_trim)
j = justification(0); // erase justification for level 0
}
else {
VERIFY(!at_base_lvl());
@ -1055,9 +993,25 @@ namespace sat {
return r;
}
void solver::propagate_clause(clause& c, bool update, unsigned assign_level, clause_offset cls_off) {
unsigned glue;
SASSERT(value(c[0]) == l_undef);
m_stats.m_propagate++;
c.mark_used();
assign_core(c[0], justification(assign_level, cls_off));
if (update && c.is_learned() && c.glue() > 2 && num_diff_levels_below(c.size(), c.begin(), c.glue() - 1, glue))
c.set_glue(glue); \
}
void solver::set_watch(clause& c, unsigned idx, clause_offset cls_off) {
std::swap(c[1], c[idx]);
DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off););
m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off));
}
bool solver::propagate_literal(literal l, bool update) {
literal l1, l2;
lbool val1, val2;
bool keep;
unsigned curr_level = lvl(l);
TRACE("sat_propagate", tout << "propagating: " << l << "@" << curr_level << " " << m_justification[l.var()] << "\n"; );
@ -1095,27 +1049,6 @@ namespace sat {
*it2 = *it;
it2++;
break;
case watched::TERNARY:
l1 = it->get_literal1();
l2 = it->get_literal2();
val1 = value(l1);
val2 = value(l2);
if (val1 == l_false && val2 == l_undef) {
m_stats.m_ter_propagate++;
assign_core(l2, justification(std::max(curr_level, lvl(l1)), l1, not_l));
}
else if (val1 == l_undef && val2 == l_false) {
m_stats.m_ter_propagate++;
assign_core(l1, justification(std::max(curr_level, lvl(l2)), l2, not_l));
}
else if (val1 == l_false && val2 == l_false) {
CONFLICT_CLEANUP();
set_conflict(justification(std::max(curr_level, lvl(l1)), l1, not_l), ~l2);
return false;
}
*it2 = *it;
it2++;
break;
case watched::CLAUSE: {
if (value(it->get_blocked_literal()) == l_true) {
TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n";
@ -1130,6 +1063,8 @@ namespace sat {
if (c[0] == not_l)
std::swap(c[0], c[1]);
CTRACE("propagate_bug", c[1] != not_l, tout << "l: " << l << " " << c << "\n";);
if (c.was_removed() || c.size() == 1 || c[1] != not_l) {
// Remark: this method may be invoked when the watch lists are not in a consistent state,
// and may contain dead/removed clauses, or clauses with removed literals.
@ -1146,58 +1081,65 @@ namespace sat {
break;
}
VERIFY(c[1] == not_l);
literal* l_it = c.begin() + 2;
literal* l_end = c.end();
unsigned undef_index = 0;
unsigned assign_level = curr_level;
unsigned max_index = 1;
for (; l_it != l_end; ++l_it) {
if (value(*l_it) != l_false) {
c[1] = *l_it;
*l_it = not_l;
DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off););
m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off));
unsigned num_undef = 0;
unsigned sz = c.size();
for (unsigned i = 2; i < sz && num_undef <= 1; ++i) {
literal lit = c[i];
switch (value(lit)) {
case l_true:
it2->set_clause(lit, cls_off);
it2++;
goto end_clause_case;
}
}
SASSERT(value(c[0]) == l_false || value(c[0]) == l_undef);
if (assign_level != scope_lvl()) {
for (unsigned i = 2; i < c.size(); ++i) {
unsigned level = lvl(c[i]);
case l_undef:
undef_index = i;
++num_undef;
break;
case l_false: {
unsigned level = lvl(lit);
if (level > assign_level) {
assign_level = level;
max_index = i;
}
break;
}
IF_VERBOSE(20, verbose_stream() << "lower assignment level " << assign_level << " scope: " << scope_lvl() << "\n");
}
}
if (value(c[0]) == l_false)
assign_level = std::max(assign_level, lvl(c[0]));
if (undef_index != 0) {
set_watch(c, undef_index, cls_off);
if (value(c[0]) == l_false && num_undef == 1) {
std::swap(c[0], c[1]);
propagate_clause(c, update, assign_level, cls_off);
}
goto end_clause_case;
}
if (value(c[0]) == l_false) {
assign_level = std::max(assign_level, lvl(c[0]));
c.mark_used();
CONFLICT_CLEANUP();
set_conflict(justification(assign_level, cls_off));
return false;
}
else {
if (max_index != 1) {
IF_VERBOSE(20, verbose_stream() << "swap watch for: " << c[1] << " " << c[max_index] << "\n");
std::swap(c[1], c[max_index]);
m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off));
}
else {
*it2 = *it;
it2++;
}
m_stats.m_propagate++;
c.mark_used();
assign_core(c[0], justification(assign_level, cls_off));
if (update && c.is_learned() && c.glue() > 2) {
unsigned glue;
if (num_diff_levels_below(c.size(), c.begin(), c.glue() - 1, glue)) {
c.set_glue(glue);
}
}
// value(c[0]) == l_undef
if (max_index != 1) {
IF_VERBOSE(20, verbose_stream() << "swap watch for: " << c[1] << " " << c[max_index] << "\n");
set_watch(c, max_index, cls_off);
}
else {
*it2 = *it;
it2++;
}
propagate_clause(c, update, assign_level, cls_off);
end_clause_case:
break;
}
@ -1322,6 +1264,7 @@ namespace sat {
if (check_inconsistent()) return l_false;
if (m_config.m_force_cleanup) do_cleanup(true);
TRACE("sat", display(tout););
TRACE("before_search", display(tout););
if (m_config.m_gc_burst) {
// force gc
@ -1358,7 +1301,6 @@ namespace sat {
return is_sat;
}
catch (const abort_solver &) {
m_reason_unknown = "sat.giveup";
IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort giveup\")\n";);
return l_undef;
}
@ -1779,6 +1721,7 @@ namespace sat {
case check_result::CR_CONTINUE:
break;
case check_result::CR_GIVEUP:
m_reason_unknown = m_ext->reason_unknown();
throw abort_solver();
}
return l_undef;
@ -2493,10 +2436,6 @@ namespace sat {
case justification::BINARY:
process_antecedent(~(js.get_literal()), num_marks);
break;
case justification::TERNARY:
process_antecedent(~(js.get_literal1()), num_marks);
process_antecedent(~(js.get_literal2()), num_marks);
break;
case justification::CLAUSE: {
clause & c = get_clause(js);
unsigned i = 0;
@ -2675,11 +2614,6 @@ namespace sat {
SASSERT(consequent != null_literal);
process_antecedent_for_unsat_core(~(js.get_literal()));
break;
case justification::TERNARY:
SASSERT(consequent != null_literal);
process_antecedent_for_unsat_core(~(js.get_literal1()));
process_antecedent_for_unsat_core(~(js.get_literal2()));
break;
case justification::CLAUSE: {
clause & c = get_clause(js);
unsigned i = 0;
@ -2816,10 +2750,6 @@ namespace sat {
case justification::BINARY:
level = update_max_level(js.get_literal(), level, unique_max);
return level;
case justification::TERNARY:
level = update_max_level(js.get_literal1(), level, unique_max);
level = update_max_level(js.get_literal2(), level, unique_max);
return level;
case justification::CLAUSE:
for (literal l : get_clause(js))
level = update_max_level(l, level, unique_max);
@ -3171,13 +3101,6 @@ namespace sat {
return false;
}
break;
case justification::TERNARY:
if (!process_antecedent_for_minimization(~(js.get_literal1())) ||
!process_antecedent_for_minimization(~(js.get_literal2()))) {
reset_unmark(old_size);
return false;
}
break;
case justification::CLAUSE: {
clause & c = get_clause(js);
unsigned i = 0;
@ -3333,10 +3256,6 @@ namespace sat {
case justification::BINARY:
update_lrb_reasoned(js.get_literal());
break;
case justification::TERNARY:
update_lrb_reasoned(js.get_literal1());
update_lrb_reasoned(js.get_literal2());
break;
case justification::CLAUSE: {
clause & c = get_clause(js);
for (literal l : c) {
@ -3406,18 +3325,6 @@ namespace sat {
unmark_lit(~l2);
}
}
else if (w.is_ternary_clause()) {
literal l2 = w.get_literal1();
literal l3 = w.get_literal2();
if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) {
// eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3
unmark_lit(~l3);
}
else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) {
// eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3
unmark_lit(~l2);
}
}
else {
// May miss some binary/ternary clauses, but that is ok.
// I sort the watch lists at every simplification round.
@ -3540,7 +3447,7 @@ namespace sat {
mark_visited(cw[j].var());
}
for (literal lit : m_lemma)
mark_visited(lit.var());
mark_visited(lit.var());
auto is_active = [&](bool_var v) {
return value(v) != l_undef && lvl(v) <= new_lvl;
@ -3562,7 +3469,7 @@ namespace sat {
auto cleanup_watch = [&](literal lit) {
for (auto const& w : get_wlist(lit)) {
IF_VERBOSE(0, verbose_stream() << "cleanup: " << lit << " " << w.is_binary_clause() << "\n");
IF_VERBOSE(1, verbose_stream() << "cleanup: " << lit << " " << w.is_binary_clause() << "\n");
}
};
for (bool_var v : m_vars_to_free) {
@ -3682,15 +3589,6 @@ namespace sat {
}
else {
clause & c = *(cw.get_clause());
if (ENABLE_TERNARY && c.size() == 3) {
if (propagate_ter_clause(c) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
else if (has_variables_to_reinit(c) && !at_base_lvl())
m_clauses_to_reinit[j++] = cw;
else
c.set_reinit_stack(false);
continue;
}
detach_clause(c);
attach_clause(c, reinit);
if (reinit && !at_base_lvl())
@ -3959,10 +3857,6 @@ namespace sat {
case justification::BINARY:
out << "binary " << js.get_literal() << "@" << lvl(js.get_literal());
break;
case justification::TERNARY:
out << "ternary " << js.get_literal1() << "@" << lvl(js.get_literal1()) << " ";
out << js.get_literal2() << "@" << lvl(js.get_literal2());
break;
case justification::CLAUSE: {
out << "(";
bool first = true;
@ -4393,7 +4287,7 @@ namespace sat {
lbool solver::get_bounded_consequences(literal_vector const& asms, bool_var_vector const& vars, vector<literal_vector>& conseq) {
bool_var_set unfixed_vars;
unsigned num_units = 0, num_iterations = 0;
unsigned num_units = 0;
for (bool_var v : vars) {
unfixed_vars.insert(v);
}
@ -4425,7 +4319,6 @@ namespace sat {
}
while (true) {
++num_iterations;
SASSERT(!inconsistent());
lbool r = bounded_search();
@ -4488,7 +4381,6 @@ namespace sat {
checkpoint();
unsigned num_resolves = 0;
unsigned num_fixed = 0;
unsigned num_assigned = 0;
lbool is_sat = l_true;
for (literal lit : unfixed_lits) {
if (value(lit) != l_undef) {
@ -4499,7 +4391,6 @@ namespace sat {
continue;
}
push();
++num_assigned;
assign_scoped(~lit);
propagate(false);
while (inconsistent()) {
@ -4622,22 +4513,14 @@ namespace sat {
if (!check_domain(lit, ~js.get_literal())) return false;
s |= m_antecedents.find(js.get_literal().var());
break;
case justification::TERNARY:
if (!check_domain(lit, ~js.get_literal1()) ||
!check_domain(lit, ~js.get_literal2())) return false;
s |= m_antecedents.find(js.get_literal1().var());
s |= m_antecedents.find(js.get_literal2().var());
break;
case justification::CLAUSE: {
clause & c = get_clause(js);
for (literal l : c) {
if (l != lit) {
if (check_domain(lit, ~l) && all_found) {
s |= m_antecedents.find(l.var());
}
else {
all_found = false;
}
if (check_domain(lit, ~l) && all_found)
s |= m_antecedents.find(l.var());
else
all_found = false;
}
}
break;
@ -4672,12 +4555,11 @@ namespace sat {
bool solver::extract_fixed_consequences1(literal lit, literal_set const& assumptions, bool_var_set& unfixed, vector<literal_vector>& conseq) {
index_set s;
if (m_antecedents.contains(lit.var())) {
if (m_antecedents.contains(lit.var()))
return true;
}
if (assumptions.contains(lit)) {
s.insert(lit.index());
}
if (assumptions.contains(lit))
s.insert(lit.index());
else {
if (!extract_assumptions(lit, s)) {
SASSERT(!m_todo_antecedents.empty());
@ -4749,7 +4631,7 @@ namespace sat {
clause_vector const & cs = *(vs[i]);
for (clause* cp : cs) {
clause & c = *cp;
if (ENABLE_TERNARY && c.size() == 3)
if (c.size() == 3)
num_ter++;
else
num_cls++;
@ -4836,22 +4718,4 @@ namespace sat {
return true;
}
void solver::init_ts(unsigned n, svector<unsigned>& v, unsigned& ts) {
if (v.empty())
ts = 0;
ts++;
if (ts == 0) {
ts = 1;
v.reset();
}
while (v.size() < n)
v.push_back(0);
}
void solver::init_visited() {
init_ts(2 * num_vars(), m_visited, m_visited_ts);
}
};

View file

@ -28,6 +28,7 @@ Revision History:
#include "util/rlimit.h"
#include "util/scoped_ptr_vector.h"
#include "util/scoped_limit_trail.h"
#include "util/visit_helper.h"
#include "sat/sat_types.h"
#include "sat/sat_clause.h"
#include "sat/sat_watched.h"
@ -174,9 +175,9 @@ namespace sat {
literal_vector m_trail;
clause_wrapper_vector m_clauses_to_reinit;
std::string m_reason_unknown;
bool m_trim = false;
svector<unsigned> m_visited;
unsigned m_visited_ts;
visit_helper m_visited;
struct scope {
unsigned m_trail_lim;
@ -203,7 +204,7 @@ namespace sat {
class lookahead* m_cuber;
class i_local_search* m_local_search;
statistics m_aux_stats;
statistics m_aux_stats;
void del_clauses(clause_vector& clauses);
@ -235,6 +236,7 @@ namespace sat {
friend class aig_finder;
friend class lut_finder;
friend class npn3_finder;
friend class proof_trim;
public:
solver(params_ref const & p, reslimit& l);
~solver() override;
@ -282,6 +284,8 @@ namespace sat {
random_gen& rand() { return m_rand; }
void set_trim() { m_trim = true; }
protected:
void reset_var(bool_var v, bool ext, bool dvar);
@ -301,9 +305,6 @@ namespace sat {
void mk_bin_clause(literal l1, literal l2, sat::status st);
void mk_bin_clause(literal l1, literal l2, bool learned) { mk_bin_clause(l1, l2, learned ? sat::status::redundant() : sat::status::asserted()); }
bool propagate_bin_clause(literal l1, literal l2);
clause * mk_ter_clause(literal * lits, status st);
bool attach_ter_clause(clause & c, status st);
bool propagate_ter_clause(clause& c);
clause * mk_nary_clause(unsigned num_lits, literal * lits, status st);
bool has_variables_to_reinit(clause const& c) const;
bool has_variables_to_reinit(literal l1, literal l2) const;
@ -339,16 +340,14 @@ namespace sat {
void detach_bin_clause(literal l1, literal l2, bool learned);
void detach_clause(clause & c);
void detach_nary_clause(clause & c);
void detach_ter_clause(clause & c);
void push_reinit_stack(clause & c);
void push_reinit_stack(literal l1, literal l2);
void init_ts(unsigned n, svector<unsigned>& v, unsigned& ts);
void init_visited();
void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; }
void init_visited(unsigned lim = 1) { m_visited.init_visited(2 * num_vars(), lim); }
bool is_visited(sat::bool_var v) const { return is_visited(literal(v, false)); }
bool is_visited(literal lit) const { return m_visited.is_visited(lit.index()); }
void mark_visited(literal lit) { m_visited.mark_visited(lit.index()); }
void mark_visited(bool_var v) { mark_visited(literal(v, false)); }
bool is_visited(bool_var v) const { return is_visited(literal(v, false)); }
bool is_visited(literal l) const { return m_visited[l.index()] == m_visited_ts; }
bool all_distinct(literal_vector const& lits);
bool all_distinct(clause const& cl);
@ -398,7 +397,7 @@ namespace sat {
}
}
void update_assign(literal l, justification j) {
if (j.level() == 0)
if (j.level() == 0 && !m_trim)
m_justification[l.var()] = j;
}
void assign_unit(literal l) { assign(l, justification(0)); }
@ -428,17 +427,17 @@ namespace sat {
}
void checkpoint() {
if (!m_checkpoint_enabled) return;
if (limit_reached()) {
if (!m_checkpoint_enabled)
return;
if (limit_reached())
throw solver_exception(Z3_CANCELED_MSG);
}
if (memory_exceeded()) {
if (memory_exceeded())
throw solver_exception(Z3_MAX_MEMORY_MSG);
}
}
void set_par(parallel* p, unsigned id);
bool canceled() { return !m_rlimit.inc(); }
config const& get_config() const { return m_config; }
void set_drat(bool d) { m_config.m_drat = d; }
drat& get_drat() { return m_drat; }
drat* get_drat_ptr() { return &m_drat; }
void set_incremental(bool b) { m_config.m_incremental = b; }
@ -476,6 +475,8 @@ namespace sat {
bool should_propagate() const;
bool propagate_core(bool update);
bool propagate_literal(literal l, bool update);
void propagate_clause(clause& c, bool update, unsigned assign_level, clause_offset cls_off);
void set_watch(clause& c, unsigned idx, clause_offset cls_off);
// -----------------------
//
@ -484,6 +485,7 @@ namespace sat {
// -----------------------
public:
lbool check(unsigned num_lits = 0, literal const* lits = nullptr);
lbool check(literal_vector const& lits) { return check(lits.size(), lits.data()); }
// retrieve model if solver return sat
model const & get_model() const { return m_model; }

View file

@ -1,6 +1,7 @@
z3_add_component(sat_solver
SOURCES
inc_sat_solver.cpp
sat_smt_solver.cpp
COMPONENT_DEPENDENCIES
aig_tactic
arith_tactics

View file

@ -26,7 +26,7 @@ Notes:
#include "solver/solver.h"
#include "solver/tactic2solver.h"
#include "solver/parallel_params.hpp"
#include "solver/parallel_tactic.h"
#include "solver/parallel_tactical.h"
#include "tactic/tactical.h"
#include "tactic/aig/aig_tactic.h"
#include "tactic/core/propagate_values_tactic.h"
@ -48,7 +48,6 @@ Notes:
// incremental SAT solver.
class inc_sat_solver : public solver {
ast_manager& m;
mutable sat::solver m_solver;
stacked_value<bool> m_has_uninterpreted;
goal2sat m_goal2sat;
@ -87,7 +86,7 @@ class inc_sat_solver : public solver {
bool is_internalized() const { return m_fmls_head == m_fmls.size(); }
public:
inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode):
m(m),
solver(m),
m_solver(p, m.limit()),
m_has_uninterpreted(false),
m_fmls(m),
@ -405,7 +404,7 @@ public:
return result;
}
proof * get_proof() override {
proof * get_proof_core() override {
return nullptr;
}
@ -464,6 +463,10 @@ public:
}
return fmls;
}
expr* congruence_next(expr* e) override { return e; }
expr* congruence_root(expr* e) override { return e; }
lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override {
init_preprocess();
@ -662,6 +665,10 @@ public:
return ext;
}
void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) override {
ensure_euf()->register_on_clause(ctx, on_clause);
}
void user_propagate_init(
void* ctx,
user_propagator::push_eh_t& push_eh,
@ -718,7 +725,8 @@ private:
if (m_solver.inconsistent())
return l_false;
m_pc.reset();
m_goal2sat(m, sz, fmls, m_params, m_solver, m_map, m_dep2asm, is_incremental());
m_goal2sat.init(m, m_params, m_solver, m_map, m_dep2asm, is_incremental());
m_goal2sat(sz, fmls);
if (!m_sat_mc) m_sat_mc = alloc(sat2goal::mc, m);
m_sat_mc->flush_smc(m_solver, m_map);
return check_uninterpreted();
@ -795,7 +803,8 @@ private:
fmls.append(sz, asms);
for (unsigned i = 0; i < get_num_assumptions(); ++i)
fmls.push_back(get_assumption(i));
m_goal2sat.assumptions(m, fmls.size(), fmls.data(), m_params, m_solver, m_map, m_dep2asm, is_incremental());
m_goal2sat.init(m, m_params, m_solver, m_map, m_dep2asm, is_incremental());
m_goal2sat.assumptions(fmls.size(), fmls.data());
extract_assumptions(fmls.size(), fmls.data());
return l_true;
}

View file

@ -0,0 +1,705 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
sat_smt_solver.cpp
Abstract:
incremental solver based on SAT core.
It uses the ast/simplifiers to allow incremental pre-processing that
produce model converters.
Author:
Nikolaj Bjorner (nbjorner) 2022-11-28
Notes:
- add back get_consequences, maybe or just have them handled by inc_sat_solver
- could also port the layered solver used by smtfd and used by get_consequences to simplifiers
--*/
#include "util/gparams.h"
#include "ast/ast_pp.h"
#include "ast/ast_translation.h"
#include "ast/ast_util.h"
#include "solver/solver.h"
#include "model/model_smt2_pp.h"
#include "model/model_evaluator.h"
#include "sat/sat_solver.h"
#include "solver/simplifier_solver.h"
#include "sat/sat_params.hpp"
#include "sat/smt/euf_solver.h"
#include "sat/tactic/goal2sat.h"
#include "sat/tactic/sat2goal.h"
#include "sat/tactic/sat_tactic.h"
#include "sat/sat_simplifier_params.hpp"
// incremental SAT solver.
class sat_smt_solver : public solver {
struct dependency2assumptions {
ast_manager& m;
trail_stack& m_trail;
expr_ref_vector m_refs;
obj_map<expr, expr*> m_dep2orig; // map original dependency to uninterpeted literal
u_map<expr*> m_lit2dep; // map from literal assumption to original expression
obj_map<expr, sat::literal> m_dep2lit; // map uninterpreted literal to sat literal
sat::literal_vector m_literals;
uint_set m_seen;
dependency2assumptions(ast_manager& m, trail_stack& t):
m(m),
m_trail(t),
m_refs(m)
{}
void reset() {
m_seen.reset();
m_literals.reset();
m_dep2lit.reset();
m_lit2dep.reset();
}
// inserted incrementally
void insert(expr* orig, expr* lit) {
m_trail.push(restore_vector(m_refs));
m_trail.push(insert_obj_map(m_dep2orig, lit));
m_refs.push_back(lit);
m_refs.push_back(orig);
m_dep2orig.insert(lit, orig);
}
// inserted on every check-sat
void insert(expr* dep, sat::literal lit) {
if (m_seen.contains(lit.index()))
return;
m_seen.insert(lit.index());
m_literals.push_back(lit);
m_dep2lit.insert(dep, lit);
m_lit2dep.insert(lit.index(), dep);
}
expr* lit2orig(sat::literal lit) {
expr* e = m_lit2dep[lit.index()];
m_dep2orig.find(e, e);
return e;
}
void copy(ast_translation& tr, dependency2assumptions const& src) {
for (auto const& [k, v] : src.m_dep2orig)
m_dep2orig.insert(tr(k), tr(v));
}
};
mutable sat::solver m_solver;
params_ref m_params;
trail_stack m_trail;
dependency2assumptions m_dep;
goal2sat m_goal2sat;
unsigned m_qhead = 0;
expr_ref_vector m_assumptions, m_core, m_ors, m_fmls, m_internalized_fmls;
atom2bool_var m_map;
mutable model_converter_ref m_cached_mc;
mutable ref<sat2goal::mc> m_sat_mc;
std::string m_unknown = "no reason given";
// access formulas after they have been pre-processed and handled by the sat solver.
// this allows to access the internal state of the SAT solver and carry on partial results.
bool m_internalized_converted = false; // have internalized formulas been converted back
bool is_internalized() const { return m_qhead == m_fmls.size(); }
public:
sat_smt_solver(ast_manager& m, params_ref const& p):
solver(m),
m_solver(p, m.limit()),
m_dep(m, m_trail),
m_assumptions(m), m_core(m), m_ors(m), m_fmls(m), m_internalized_fmls(m),
m_map(m) {
updt_params(p);
m_solver.set_incremental(true);
}
solver* translate(ast_manager& dst_m, params_ref const& p) override {
if (m_trail.get_num_scopes() > 0)
throw default_exception("Cannot translate sat solver at non-base level");
ast_translation tr(m, dst_m);
m_solver.pop_to_base_level();
sat_smt_solver* result = alloc(sat_smt_solver, dst_m, p);
auto* ext = get_euf();
if (ext) {
auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep.m_dep2lit, true);
euf::solver::scoped_set_translate st(*ext, dst_m, si);
result->m_solver.copy(m_solver);
}
else {
result->m_solver.copy(m_solver);
}
// TODO: copy preprocess state
for (auto const& [k, v] : m_dep.m_dep2orig) result->m_dep.insert(tr(v), tr(k));
for (expr* f : m_assumptions) result->m_assumptions.push_back(tr(f));
for (auto & kv : m_map) result->m_map.insert(tr(kv.m_key), kv.m_value);
for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f));
result->m_dep.copy(tr, m_dep);
result->m_internalized_converted = m_internalized_converted;
return result;
}
void set_progress_callback(progress_callback * callback) override {}
void init_check_sat() {
m_solver.pop_to_base_level();
m_core.reset();
m_dep.reset();
m_cached_mc = nullptr;
init_reason_unknown();
m_internalized_converted = false;
}
lbool check_sat_core(unsigned sz, expr * const * _assumptions) override {
init_check_sat();
if (m_solver.inconsistent())
return l_false;
expr_ref_vector assumptions(m);
for (unsigned i = 0; i < sz; ++i)
assumptions.push_back(ensure_literal(_assumptions[i]));
TRACE("sat", tout << assumptions << "\n";);
lbool r = internalize_formulas(assumptions);
if (r != l_true)
return r;
internalize_assumptions(assumptions);
try {
r = m_solver.check(m_dep.m_literals);
}
catch (z3_exception& ex) {
IF_VERBOSE(1, verbose_stream() << "exception: " << ex.msg() << "\n";);
if (m.inc()) {
set_reason_unknown(std::string("(sat.giveup ") + ex.msg() + ')');
return l_undef;
}
r = l_undef;
}
switch (r) {
case l_true:
check_assumptions();
break;
case l_false:
extract_core();
break;
default:
set_reason_unknown(m_solver.get_reason_unknown());
break;
}
return r;
}
void push() override {
try {
expr_ref_vector none(m);
internalize_formulas(none);
}
catch (...) {
push_internal();
throw;
}
push_internal();
}
void push_internal() {
m_solver.user_push();
m_goal2sat.user_push();
m_map.push();
m_trail.push_scope();
m_trail.push(restore_vector(m_assumptions));
m_trail.push(restore_vector(m_fmls));
m_trail.push(value_trail(m_qhead));
}
void pop(unsigned n) override {
n = std::min(n, m_trail.get_num_scopes()); // allow sat_smt_solver to take over for another solver.
m_trail.pop_scope(n);
m_map.pop(n);
m_goal2sat.user_pop(n);
m_solver.user_pop(n);
}
void set_phase(expr* e) override {
bool is_not = m.is_not(e, e);
sat::bool_var b = m_map.to_bool_var(e);
if (b != sat::null_bool_var)
m_solver.set_phase(sat::literal(b, is_not));
}
class sat_phase : public phase, public sat::literal_vector {};
phase* get_phase() override {
sat_phase* p = alloc(sat_phase);
for (unsigned v = m_solver.num_vars(); v-- > 0; )
p->push_back(sat::literal(v, !m_solver.get_phase(v)));
return p;
}
void set_phase(phase* p) override {
for (auto lit : *static_cast<sat_phase*>(p))
m_solver.set_phase(lit);
}
void move_to_front(expr* e) override {
m.is_not(e, e);
sat::bool_var b = m_map.to_bool_var(e);
if (b != sat::null_bool_var)
m_solver.move_to_front(b);
}
unsigned get_scope_level() const override {
return m_trail.get_num_scopes();
}
bool is_literal(expr* a) const {
m.is_not(a, a);
return is_uninterp_const(a);
}
/*
* Ensure dependencies are literals so that pre-processing can apply to them.
*/
expr* ensure_literal(expr* a) {
if (is_literal(a))
return a;
expr* new_dep = m.mk_fresh_const("dep", m.mk_bool_sort());
expr* fml = m.mk_iff(new_dep, a);
m_fmls.push_back(fml);
m_dep.insert(a, new_dep);
return new_dep;
}
void assert_expr_core2(expr * t, expr * a) override {
m_ors.reset();
m_ors.push_back(t);
if (m.is_and(a)) {
for (expr* arg : *to_app(a)) {
arg = ensure_literal(arg);
m_ors.push_back(mk_not(m, arg));
m_assumptions.push_back(arg);
}
}
else {
a = ensure_literal(a);
m_assumptions.push_back(a);
m_ors.push_back(mk_not(m, a));
}
flatten_or(m_ors);
m_fmls.push_back(mk_or(m_ors));
}
void assert_expr_core(expr * t) override {
m_fmls.push_back(t);
}
ast_manager& get_manager() const override { return m; }
void set_produce_models(bool f) override {}
void collect_param_descrs(param_descrs & r) override {
solver::collect_param_descrs(r);
goal2sat::collect_param_descrs(r);
sat::solver::collect_param_descrs(r);
}
void updt_params(params_ref const & p) override {
m_params.append(p);
sat_params sp(p);
m_params.set_bool("keep_cardinality_constraints", sp.cardinality_solver());
m_params.set_sym("pb.solver", sp.pb_solver());
m_solver.updt_params(m_params);
m_solver.set_incremental(true);
if (sp.smt())
ensure_euf();
}
void collect_statistics(statistics & st) const override {
m_solver.collect_statistics(st);
}
void get_unsat_core(expr_ref_vector & r) override {
r.reset();
r.append(m_core.size(), m_core.data());
}
void get_levels(ptr_vector<expr> const& vars, unsigned_vector& depth) override {
unsigned sz = vars.size();
depth.resize(sz);
for (unsigned i = 0; i < sz; ++i) {
auto bv = m_map.to_bool_var(vars[i]);
depth[i] = bv == sat::null_bool_var ? UINT_MAX : m_solver.lvl(bv);
}
}
expr_ref_vector get_trail(unsigned max_level) override {
expr_ref_vector result(m), lit2expr(m);
unsigned sz = m_solver.trail_size();
lit2expr.resize(m_solver.num_vars() * 2);
m_map.mk_inv(lit2expr);
for (unsigned i = 0; i < sz; ++i) {
sat::literal lit = m_solver.trail_literal(i);
if (m_solver.lvl(lit) > max_level)
continue;
expr_ref e(lit2expr.get(lit.index()), m);
if (e)
result.push_back(e);
}
return result;
}
proof * get_proof_core() override {
return nullptr;
}
expr_ref_vector last_cube(bool is_sat) {
expr_ref_vector result(m);
result.push_back(is_sat ? m.mk_true() : m.mk_false());
return result;
}
expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override {
lbool r = internalize_formulas(vs);
if (r != l_true) {
IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n");
return expr_ref_vector(m);
}
convert_internalized();
if (m_solver.inconsistent())
return last_cube(false);
obj_hashtable<expr> _vs;
for (expr* v : vs)
_vs.insert(v);
sat::bool_var_vector vars;
for (auto& kv : m_map)
if (_vs.empty() || _vs.contains(kv.m_key))
vars.push_back(kv.m_value);
sat::literal_vector lits;
lbool result = m_solver.cube(vars, lits, backtrack_level);
expr_ref_vector fmls(m);
expr_ref_vector lit2expr(m);
lit2expr.resize(m_solver.num_vars() * 2);
m_map.mk_inv(lit2expr);
for (sat::literal l : lits) {
expr* e = lit2expr.get(l.index());
SASSERT(e);
fmls.push_back(e);
}
vs.reset();
for (sat::bool_var v : vars) {
expr* x = lit2expr[sat::literal(v, false).index()].get();
if (x)
vs.push_back(x);
}
switch (result) {
case l_true:
return last_cube(true);
case l_false:
return last_cube(false);
default:
break;
}
if (lits.empty())
set_reason_unknown(m_solver.get_reason_unknown());
return fmls;
}
expr* congruence_next(expr* e) override { return e; }
expr* congruence_root(expr* e) override { return e; }
lbool find_mutexes(expr_ref_vector const& vars, vector<expr_ref_vector>& mutexes) override {
sat::literal_vector ls;
u_map<expr*> lit2var;
for (expr * e : vars) {
expr* atom = e;;
bool neg = m.is_not(e, atom);
sat::bool_var v = m_map.to_bool_var(atom);
if (v != sat::null_bool_var) {
sat::literal lit(v, neg);
ls.push_back(lit);
lit2var.insert(lit.index(), e);
}
}
vector<sat::literal_vector> ls_mutexes;
m_solver.find_mutexes(ls, ls_mutexes);
for (sat::literal_vector const& ls_mutex : ls_mutexes) {
expr_ref_vector mutex(m);
for (sat::literal l : ls_mutex)
mutex.push_back(lit2var.find(l.index()));
mutexes.push_back(mutex);
}
return l_true;
}
void init_reason_unknown() {
m_unknown = "no reason given";
}
std::string reason_unknown() const override {
return m_unknown;
}
void set_reason_unknown(char const* msg) override {
m_unknown = msg;
}
void set_reason_unknown(std::string &&msg) {
m_unknown = std::move(msg);
}
void get_labels(svector<symbol> & r) override {
}
unsigned get_num_assertions() const override {
const_cast<sat_smt_solver*>(this)->convert_internalized();
if (is_internalized() && m_internalized_converted)
return m_internalized_fmls.size();
else
return m_fmls.size();
}
expr * get_assertion(unsigned idx) const override {
if (is_internalized() && m_internalized_converted)
return m_internalized_fmls[idx];
return m_fmls.get(idx);
}
unsigned get_num_assumptions() const override {
return m_assumptions.size();
}
expr * get_assumption(unsigned idx) const override {
return m_assumptions[idx];
}
model_converter_ref get_model_converter() const override {
const_cast<sat_smt_solver*>(this)->convert_internalized();
verbose_stream() << "get model converter " << (m_cached_mc.get() != nullptr) << "\n";
if (m_cached_mc)
return m_cached_mc;
if (is_internalized() && m_internalized_converted) {
if (m_sat_mc) m_sat_mc->flush_smc(m_solver, m_map);
m_cached_mc = concat(solver::get_model_converter().get(), m_sat_mc.get());
TRACE("sat", m_cached_mc->display(tout););
return m_cached_mc;
}
else {
return solver::get_model_converter();
}
}
void convert_internalized() {
m_solver.pop_to_base_level();
expr_ref_vector none(m);
internalize_formulas(none);
if (!is_internalized() || m_internalized_converted)
return;
sat2goal s2g;
m_cached_mc = nullptr;
goal g(m, false, true, false);
s2g(m_solver, m_map, m_params, g, m_sat_mc);
m_internalized_fmls.reset();
g.get_formulas(m_internalized_fmls);
TRACE("sat", m_solver.display(tout); tout << m_internalized_fmls << "\n";);
m_internalized_converted = true;
}
euf::solver* get_euf() {
return dynamic_cast<euf::solver*>(m_solver.get_extension());
}
void init_goal2sat() {
m_goal2sat.init(m, m_params, m_solver, m_map, m_dep.m_dep2lit, true);
}
euf::solver* ensure_euf() {
init_goal2sat();
return m_goal2sat.ensure_euf();
}
void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) override {
ensure_euf()->register_on_clause(ctx, on_clause);
}
void user_propagate_init(
void* ctx,
user_propagator::push_eh_t& push_eh,
user_propagator::pop_eh_t& pop_eh,
user_propagator::fresh_eh_t& fresh_eh) override {
ensure_euf()->user_propagate_init(ctx, push_eh, pop_eh, fresh_eh);
}
void user_propagate_register_fixed(user_propagator::fixed_eh_t& fixed_eh) override {
ensure_euf()->user_propagate_register_fixed(fixed_eh);
}
void user_propagate_register_final(user_propagator::final_eh_t& final_eh) override {
ensure_euf()->user_propagate_register_final(final_eh);
}
void user_propagate_register_eq(user_propagator::eq_eh_t& eq_eh) override {
ensure_euf()->user_propagate_register_eq(eq_eh);
}
void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override {
ensure_euf()->user_propagate_register_diseq(diseq_eh);
}
void user_propagate_register_expr(expr* e) override {
ensure_euf()->user_propagate_register_expr(e);
}
void user_propagate_register_created(user_propagator::created_eh_t& r) override {
ensure_euf()->user_propagate_register_created(r);
}
private:
void add_assumption(expr* a) {
init_goal2sat();
m_dep.insert(a, m_goal2sat.internalize(a));
}
void internalize_assumptions(expr_ref_vector const& asms) {
for (expr* a : asms)
add_assumption(a);
for (expr* a : m_assumptions)
add_assumption(a);
}
lbool internalize_formulas(expr_ref_vector& assumptions) {
if (is_internalized() && assumptions.empty())
return l_true;
TRACE("sat", tout << "qhead " << m_qhead << "\n");
m_internalized_converted = false;
m_solver.pop_to_base_level();
init_goal2sat();
m_goal2sat(m_fmls.size() - m_qhead, m_fmls.data() + m_qhead);
if (!m_sat_mc)
m_sat_mc = alloc(sat2goal::mc, m);
m_sat_mc->flush_smc(m_solver, m_map);
m_qhead = m_fmls.size();
return m.inc() ? l_true : l_undef;
}
void extract_core() {
m_core.reset();
if (m_dep.m_literals.empty())
return;
for (sat::literal c : m_solver.get_core())
m_core.push_back(m_dep.lit2orig(c));
TRACE("sat",
tout << "core: " << m_solver.get_core() << "\n";
tout << "core: " << m_core << "\n";
m_solver.display(tout));
}
void check_assumptions() {
sat::model const& ll_m = m_solver.get_model();
for (auto const& [k, lit] : m_dep.m_dep2lit) {
if (sat::value_at(lit, ll_m) == l_true)
continue;
IF_VERBOSE(0, verbose_stream() << mk_pp(k, m) << " does not evaluate to true\n";
verbose_stream() << m_dep.m_literals << "\n";
m_solver.display_assignment(verbose_stream());
m_solver.display(verbose_stream()););
throw default_exception("bad state");
}
}
void get_model_core(model_ref & mdl) override {
TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";);
mdl = nullptr;
if (!m_solver.model_is_current())
return;
if (m_fmls.size() > m_qhead)
return;
TRACE("sat", m_solver.display_model(tout););
CTRACE("sat", m_sat_mc, m_sat_mc->display(tout););
sat::model ll_m = m_solver.get_model();
mdl = alloc(model, m);
if (m_sat_mc)
(*m_sat_mc)(ll_m);
expr_ref_vector var2expr(m);
m_map.mk_var_inv(var2expr);
for (unsigned v = 0; v < var2expr.size(); ++v) {
expr * n = var2expr.get(v);
if (!n || !is_uninterp_const(n))
continue;
switch (sat::value_at(v, ll_m)) {
case l_true:
mdl->register_decl(to_app(n)->get_decl(), m.mk_true());
break;
case l_false:
mdl->register_decl(to_app(n)->get_decl(), m.mk_false());
break;
default:
break;
}
}
TRACE("sat", m_solver.display(tout););
if (m_sat_mc)
(*m_sat_mc)(mdl);
m_goal2sat.update_model(mdl);
TRACE("sat", model_smt2_pp(tout, m, *mdl, 0););
if (gparams::get_ref().get_bool("model_validate", false)) {
IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";);
model_evaluator eval(*mdl);
eval.set_model_completion(true);
bool all_true = true;
for (expr* f : m_fmls) {
if (has_quantifiers(f))
continue;
expr_ref tmp(m);
eval(f, tmp);
if (m.limit().is_canceled())
return;
CTRACE("sat", !m.is_true(tmp),
tout << "Evaluation failed: " << mk_pp(f, m) << " to " << tmp << "\n";
model_smt2_pp(tout, m, *(mdl.get()), 0););
if (m.is_false(tmp)) {
IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(f, m) << "\n");
IF_VERBOSE(0, verbose_stream() << "evaluated to " << tmp << "\n");
all_true = false;
}
}
if (!all_true) {
IF_VERBOSE(0, verbose_stream() << m_params << "\n");
IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n");
exit(0);
}
else {
IF_VERBOSE(1, verbose_stream() << "solution verified\n");
}
}
}
};
solver* mk_sat_smt_solver(ast_manager& m, params_ref const& p) {
return mk_simplifier_solver(alloc(sat_smt_solver, m, p), nullptr);
}

View file

@ -0,0 +1,25 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
sat_smt_solver.h
Abstract:
incremental solver based on SAT core.
Author:
Nikolaj Bjorner (nbjorner) 2014-7-30
Notes:
--*/
#pragma once
#include "solver/solver.h"
solver* mk_sat_smt_solver(ast_manager& m, params_ref const& p);

View file

@ -71,32 +71,6 @@ namespace sat {
VERIFY(found);
}
void erase_ternary_watch(watch_list& wlist, literal l1, literal l2) {
watched w(l1, l2);
watch_list::iterator it = wlist.begin(), end = wlist.end();
watch_list::iterator it2 = it;
bool found = false;
for (; it != end; ++it) {
if (!found && w == *it) {
found = true;
}
else {
*it2 = *it;
++it2;
}
}
wlist.set_end(it2);
#if 0
VERIFY(found);
for (watched const& w2 : wlist) {
if (w2 == w) {
std::cout << l1 << " " << l2 << "\n";
}
//VERIFY(w2 != w);
}
#endif
}
void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist) {
watch_list::iterator end = wlist.end();
for (; it != end; ++it, ++it2)
@ -118,9 +92,6 @@ namespace sat {
if (w.is_learned())
out << "*";
break;
case watched::TERNARY:
out << "(" << w.get_literal1() << " " << w.get_literal2() << ")";
break;
case watched::CLAUSE:
out << "(" << w.get_blocked_literal() << " " << *(ca.get_clause(w.get_clause_offset())) << ")";
break;

View file

@ -40,7 +40,7 @@ namespace sat {
class watched {
public:
enum kind {
BINARY = 0, TERNARY, CLAUSE, EXT_CONSTRAINT
BINARY = 0, CLAUSE, EXT_CONSTRAINT
};
private:
size_t m_val1;
@ -55,16 +55,6 @@ namespace sat {
SASSERT(learned || is_binary_non_learned_clause());
}
watched(literal l1, literal l2) {
SASSERT(l1 != l2);
if (l1.index() > l2.index())
std::swap(l1, l2);
m_val1 = l1.to_uint();
m_val2 = static_cast<unsigned>(TERNARY) + (l2.to_uint() << 2);
SASSERT(is_ternary_clause());
SASSERT(get_literal1() == l1);
SASSERT(get_literal2() == l2);
}
unsigned val2() const { return m_val2; }
@ -95,9 +85,6 @@ namespace sat {
void set_learned(bool l) { if (l) m_val2 |= 4u; else m_val2 &= ~4u; SASSERT(is_learned() == l); }
bool is_ternary_clause() const { return get_kind() == TERNARY; }
literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(static_cast<unsigned>(m_val1)); }
literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); }
bool is_clause() const { return get_kind() == CLAUSE; }
clause_offset get_clause_offset() const { SASSERT(is_clause()); return static_cast<clause_offset>(m_val1); }
@ -116,17 +103,14 @@ namespace sat {
bool operator!=(watched const & w) const { return !operator==(w); }
};
static_assert(0 <= watched::BINARY && watched::BINARY <= 3, "");
static_assert(0 <= watched::TERNARY && watched::TERNARY <= 3, "");
static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 3, "");
static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3, "");
static_assert(0 <= watched::BINARY && watched::BINARY <= 2, "");
static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 2, "");
static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 2, "");
struct watched_lt {
bool operator()(watched const & w1, watched const & w2) const {
if (w2.is_binary_clause()) return false;
if (w1.is_binary_clause()) return true;
if (w2.is_ternary_clause()) return false;
if (w1.is_ternary_clause()) return true;
return false;
}
};
@ -136,8 +120,6 @@ namespace sat {
watched* find_binary_watch(watch_list & wlist, literal l);
watched const* find_binary_watch(watch_list const & wlist, literal l);
bool erase_clause_watch(watch_list & wlist, clause_offset c);
void erase_ternary_watch(watch_list & wlist, literal l1, literal l2);
void set_ternary_learned(watch_list& wlist, literal l1, literal l2, bool learned);
class clause_allocator;
std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist, extension* ext);

View file

@ -122,8 +122,8 @@ namespace sat {
}
bool xor_finder::extract_xor(bool parity, clause& c, literal l1, literal l2) {
SASSERT(s.is_visited(l1.var()));
SASSERT(s.is_visited(l2.var()));
SASSERT(s.m_visited.is_visited(l1.var()));
SASSERT(s.m_visited.is_visited(l2.var()));
m_missing.reset();
unsigned mask = 0;
for (unsigned i = 0; i < c.size(); ++i) {

View file

@ -16,6 +16,7 @@ z3_add_component(sat_smt
bv_invariant.cpp
bv_polysat.cpp
bv_solver.cpp
bv_theory_checker.cpp
dt_solver.cpp
euf_ackerman.cpp
euf_internalize.cpp
@ -37,10 +38,12 @@ z3_add_component(sat_smt
q_mam.cpp
q_mbi.cpp
q_model_fixer.cpp
q_theory_checker.cpp
q_queue.cpp
q_solver.cpp
recfun_solver.cpp
sat_th.cpp
tseitin_theory_checker.cpp
user_solver.cpp
COMPONENT_DEPENDENCIES
sat

View file

@ -251,6 +251,17 @@ namespace arith {
if (hi_sup != end) mk_bound_axiom(b, *hi_sup);
}
void solver::add_farkas_clause(sat::literal l1, sat::literal l2) {
arith_proof_hint* bound_params = nullptr;
if (ctx.use_drat()) {
m_arith_hint.set_type(ctx, hint_type::farkas_h);
m_arith_hint.add_lit(rational(1), ~l1);
m_arith_hint.add_lit(rational(1), ~l2);
bound_params = m_arith_hint.mk(ctx);
}
add_clause(l1, l2, bound_params);
}
void solver::mk_bound_axiom(api_bound& b1, api_bound& b2) {
literal l1(b1.get_lit());
literal l2(b2.get_lit());
@ -263,55 +274,45 @@ namespace arith {
if (k1 == k2 && kind1 == kind2) return;
SASSERT(k1 != k2 || kind1 != kind2);
auto bin_clause = [&](sat::literal l1, sat::literal l2) {
arith_proof_hint* bound_params = nullptr;
if (ctx.use_drat()) {
m_arith_hint.set_type(ctx, hint_type::farkas_h);
m_arith_hint.add_lit(rational(1), ~l1);
m_arith_hint.add_lit(rational(1), ~l2);
bound_params = m_arith_hint.mk(ctx);
}
add_clause(l1, l2, bound_params);
};
if (kind1 == lp_api::lower_t) {
if (kind2 == lp_api::lower_t) {
if (k2 <= k1)
bin_clause(~l1, l2);
add_farkas_clause(~l1, l2);
else
bin_clause(l1, ~l2);
add_farkas_clause(l1, ~l2);
}
else if (k1 <= k2)
// k1 <= k2, k1 <= x or x <= k2
bin_clause(l1, l2);
add_farkas_clause(l1, l2);
else {
// k1 > hi_inf, k1 <= x => ~(x <= hi_inf)
bin_clause(~l1, ~l2);
add_farkas_clause(~l1, ~l2);
if (v_is_int && k1 == k2 + rational(1))
// k1 <= x or x <= k1-1
bin_clause(l1, l2);
add_farkas_clause(l1, l2);
}
}
else if (kind2 == lp_api::lower_t) {
if (k1 >= k2)
// k1 >= lo_inf, k1 >= x or lo_inf <= x
bin_clause(l1, l2);
add_farkas_clause(l1, l2);
else {
// k1 < k2, k2 <= x => ~(x <= k1)
bin_clause(~l1, ~l2);
add_farkas_clause(~l1, ~l2);
if (v_is_int && k1 == k2 - rational(1))
// x <= k1 or k1+l <= x
bin_clause(l1, l2);
add_farkas_clause(l1, l2);
}
}
else {
// kind1 == A_UPPER, kind2 == A_UPPER
if (k1 >= k2)
// k1 >= k2, x <= k2 => x <= k1
bin_clause(l1, ~l2);
add_farkas_clause(l1, ~l2);
else
// k1 <= hi_sup , x <= k1 => x <= hi_sup
bin_clause(~l1, l2);
add_farkas_clause(~l1, l2);
}
}
@ -421,9 +422,9 @@ namespace arith {
ge = mk_literal(a.mk_ge(diff, zero));
}
++m_stats.m_assert_diseq;
add_clause(~eq, le);
add_clause(~eq, ge);
add_clause(~le, ~ge, eq);
add_farkas_clause(~eq, le);
add_farkas_clause(~eq, ge);
add_clause(~le, ~ge, eq, explain_trichotomy(le, ge, eq));
}

View file

@ -69,7 +69,7 @@ namespace arith {
}
std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const {
return euf::th_explain::from_index(idx).display(out);
return euf::th_explain::from_index(idx).display(out << "arith ");
}
std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const {
@ -82,10 +82,8 @@ namespace arith {
if (m_nla) m_nla->collect_statistics(st);
}
void solver::explain_assumptions() {
unsigned i = 0;
for (auto const & ev : m_explanation) {
++i;
void solver::explain_assumptions(lp::explanation const& e) {
for (auto const & ev : e) {
auto idx = ev.ci();
if (UINT_MAX == idx)
continue;
@ -118,27 +116,54 @@ namespace arith {
if (!ctx.use_drat())
return nullptr;
m_arith_hint.set_type(ctx, ty);
explain_assumptions();
explain_assumptions(m_explanation);
if (lit != sat::null_literal)
m_arith_hint.add_lit(rational(1), ~lit);
return m_arith_hint.mk(ctx);
}
arith_proof_hint const* solver::explain_implied_eq(euf::enode* a, euf::enode* b) {
arith_proof_hint const* solver::explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs) {
arith_proof_hint* hint = nullptr;
if (ctx.use_drat()) {
m_arith_hint.set_type(ctx, hint_type::farkas_h);
for (auto lit : core)
m_arith_hint.add_lit(rational::one(), lit);
for (auto const& [a,b] : eqs)
m_arith_hint.add_eq(a, b);
hint = m_arith_hint.mk(ctx);
}
return hint;
}
arith_proof_hint const* solver::explain_implied_eq(lp::explanation const& e, euf::enode* a, euf::enode* b) {
if (!ctx.use_drat())
return nullptr;
m_arith_hint.set_type(ctx, hint_type::implied_eq_h);
explain_assumptions();
explain_assumptions(e);
m_arith_hint.set_num_le(1); // TODO
m_arith_hint.add_diseq(a, b);
return m_arith_hint.mk(ctx);
}
arith_proof_hint const* solver::explain_trichotomy(sat::literal le, sat::literal ge, sat::literal eq) {
if (!ctx.use_drat())
return nullptr;
m_arith_hint.set_type(ctx, hint_type::implied_eq_h);
m_arith_hint.set_num_le(1);
m_arith_hint.add_lit(rational(1), le);
m_arith_hint.add_lit(rational(1), ge);
m_arith_hint.add_lit(rational(1), ~eq);
return m_arith_hint.mk(ctx);
}
expr* arith_proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
family_id fid = m.get_family_id("arith");
arith_util arith(m);
solver& a = dynamic_cast<solver&>(*s.fid2solver(fid));
char const* name;
expr_ref_vector args(m);
switch (m_ty) {
case hint_type::farkas_h:
name = "farkas";
@ -148,15 +173,14 @@ namespace arith {
break;
case hint_type::implied_eq_h:
name = "implied-eq";
args.push_back(arith.mk_int(m_num_le));
break;
}
rational lc(1);
for (unsigned i = m_lit_head; i < m_lit_tail; ++i)
lc = lcm(lc, denominator(a.m_arith_hint.lit(i).first));
expr_ref_vector args(m);
sort_ref_vector sorts(m);
for (unsigned i = m_lit_head; i < m_lit_tail; ++i) {
for (unsigned i = m_lit_head; i < m_lit_tail; ++i) {
auto const& [coeff, lit] = a.m_arith_hint.lit(i);
args.push_back(arith.mk_int(abs(coeff*lc)));
args.push_back(s.literal2expr(lit));
@ -168,11 +192,6 @@ namespace arith {
args.push_back(arith.mk_int(1));
args.push_back(eq);
}
for (expr* arg : args)
sorts.push_back(arg->get_sort());
sort* range = m.mk_proof_sort();
func_decl* d = m.mk_func_decl(symbol(name), args.size(), sorts.data(), range);
expr* r = m.mk_app(d, args);
return r;
return m.mk_app(symbol(name), args.size(), args.data(), m.mk_proof_sort());
}
}

View file

@ -20,9 +20,8 @@ Author:
namespace arith {
sat::literal solver::internalize(expr* e, bool sign, bool root, bool learned) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
init_internalize();
flet<bool> _is_learned(m_is_redundant, learned);
internalize_atom(e);
literal lit = ctx.expr2literal(e);
if (sign)
@ -30,9 +29,8 @@ namespace arith {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
void solver::internalize(expr* e) {
init_internalize();
flet<bool> _is_learned(m_is_redundant, redundant);
if (m.is_bool(e))
internalize_atom(e);
else
@ -98,6 +96,7 @@ namespace arith {
void solver::found_underspecified(expr* n) {
if (a.is_underspecified(n)) {
TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";);
ctx.push(push_back_vector(m_underspecified));
m_underspecified.push_back(to_app(n));
}
expr* e = nullptr, * x = nullptr, * y = nullptr;
@ -243,9 +242,10 @@ namespace arith {
mk_abs_axiom(t);
else if (a.is_idiv(n, n1, n2)) {
if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n);
ctx.push(push_back_vector(m_idiv_terms));
m_idiv_terms.push_back(n);
app_ref mod(a.mk_mod(n1, n2), m);
internalize(mod, m_is_redundant);
internalize(mod);
st.to_ensure_var().push_back(n1);
st.to_ensure_var().push_back(n2);
}
@ -372,7 +372,7 @@ namespace arith {
enode* n = ctx.get_enode(atom);
theory_var w = mk_var(n);
ctx.attach_th_var(n, this, w);
ctx.get_egraph().set_merge_enabled(n, false);
ctx.get_egraph().set_cgc_enabled(n, false);
if (is_int(v) && !r.is_int())
r = (k == lp_api::upper_t) ? floor(r) : ceil(r);
api_bound* b = mk_var_bound(lit, v, k, r);

View file

@ -76,7 +76,6 @@ namespace arith {
}
bool solver::unit_propagate() {
TRACE("arith", tout << "unit propagate\n";);
m_model_is_initialized = false;
if (!m_solver->has_changed_columns() && !m_new_eq && m_new_bounds.empty() && m_asserted_qhead == m_asserted.size())
return false;
@ -320,7 +319,7 @@ namespace arith {
reset_evidence();
for (auto ev : e)
set_evidence(ev.ci());
auto* ex = explain_implied_eq(n1, n2);
auto* ex = explain_implied_eq(e, n1, n2);
auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2, ex);
ctx.propagate(n1, n2, jst->to_index());
return true;
@ -562,6 +561,9 @@ namespace arith {
void solver::dbg_finalize_model(model& mdl) {
if (m_not_handled)
return;
// this is already handled in general in euf_model.cpp
return;
bool found_bad = false;
for (unsigned v = 0; v < get_num_vars(); ++v) {
if (!is_bool(v))
@ -584,35 +586,8 @@ namespace arith {
if (!found_bad && value == get_phase(n->bool_var()))
continue;
TRACE("arith",
ptr_vector<expr> nodes;
expr_mark marks;
nodes.push_back(n->get_expr());
for (unsigned i = 0; i < nodes.size(); ++i) {
expr* r = nodes[i];
if (marks.is_marked(r))
continue;
marks.mark(r);
if (is_app(r))
for (expr* arg : *to_app(r))
nodes.push_back(arg);
expr_ref rval(m);
expr_ref mval = mdl(r);
if (ctx.get_egraph().find(r))
rval = mdl(ctx.get_egraph().find(r)->get_root()->get_expr());
tout << r->get_id() << ": " << mk_bounded_pp(r, m, 1) << " := " << mval;
if (rval != mval) tout << " " << rval;
tout << "\n";
});
TRACE("arith",
tout << eval << " " << value << " " << ctx.bpp(n) << "\n";
tout << mdl << "\n";
s().display(tout););
IF_VERBOSE(0,
verbose_stream() << eval << " " << value << " " << ctx.bpp(n) << "\n";
verbose_stream() << n->bool_var() << " " << n->value() << " " << get_phase(n->bool_var()) << " " << ctx.bpp(n) << "\n";
verbose_stream() << *b << "\n";);
IF_VERBOSE(0, ctx.display_validation_failure(verbose_stream(), mdl, n));
TRACE("arith", ctx.display_validation_failure(tout << *b << "\n", mdl, n));
IF_VERBOSE(0, ctx.display_validation_failure(verbose_stream() << *b << "\n", mdl, n));
UNREACHABLE();
}
}
@ -634,7 +609,7 @@ namespace arith {
else if (v != euf::null_theory_var) {
rational r = get_value(v);
TRACE("arith", tout << mk_pp(o, m) << " v" << v << " := " << r << "\n";);
SASSERT("integer variables should have integer values: " && (!a.is_int(o) || r.is_int() || m.limit().is_canceled()));
SASSERT("integer variables should have integer values: " && (ctx.get_config().m_arith_ignore_int || !a.is_int(o) || r.is_int() || m.limit().is_canceled()));
if (a.is_int(o) && !r.is_int())
r = floor(r);
value = a.mk_numeral(r, o->get_sort());
@ -681,26 +656,19 @@ namespace arith {
scope& sc = m_scopes.back();
sc.m_bounds_lim = m_bounds_trail.size();
sc.m_asserted_qhead = m_asserted_qhead;
sc.m_idiv_lim = m_idiv_terms.size();
sc.m_asserted_lim = m_asserted.size();
sc.m_not_handled = m_not_handled;
sc.m_underspecified_lim = m_underspecified.size();
lp().push();
if (m_nla)
m_nla->push();
th_euf_solver::push_core();
}
void solver::pop_core(unsigned num_scopes) {
TRACE("arith", tout << "pop " << num_scopes << "\n";);
unsigned old_size = m_scopes.size() - num_scopes;
del_bounds(m_scopes[old_size].m_bounds_lim);
m_idiv_terms.shrink(m_scopes[old_size].m_idiv_lim);
m_asserted.shrink(m_scopes[old_size].m_asserted_lim);
m_asserted_qhead = m_scopes[old_size].m_asserted_qhead;
m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim);
m_not_handled = m_scopes[old_size].m_not_handled;
m_scopes.resize(old_size);
lp().pop(num_scopes);
m_new_bounds.reset();
@ -751,7 +719,7 @@ namespace arith {
set_evidence(ci4);
enode* x = var2enode(v1);
enode* y = var2enode(v2);
auto* ex = explain_implied_eq(x, y);
auto* ex = explain_implied_eq(m_explanation, x, y);
auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, x, y, ex);
ctx.propagate(x, y, jst->to_index());
}
@ -925,11 +893,11 @@ namespace arith {
theory_var other = m_model_eqs.insert_if_not_there(v);
TRACE("arith", tout << "insert: v" << v << " := " << get_value(v) << " found: v" << other << "\n";);
if (!is_equal(other, v))
m_assume_eq_candidates.push_back(std::make_pair(v, other));
m_assume_eq_candidates.push_back({ v, other });
}
if (m_assume_eq_candidates.size() > old_sz)
ctx.push(restore_size_trail<std::pair<theory_var, theory_var>, false>(m_assume_eq_candidates, old_sz));
ctx.push(restore_vector(m_assume_eq_candidates, old_sz));
return delayed_assume_eqs();
}
@ -984,7 +952,6 @@ namespace arith {
sat::check_result solver::check() {
force_push();
m_model_is_initialized = false;
flet<bool> _is_learned(m_is_redundant, true);
IF_VERBOSE(12, verbose_stream() << "final-check " << lp().get_status() << "\n");
SASSERT(lp().ax_is_correct());
@ -1004,6 +971,7 @@ namespace arith {
}
auto st = sat::check_result::CR_DONE;
bool int_undef = false;
TRACE("arith", ctx.display(tout););
@ -1017,9 +985,7 @@ namespace arith {
return sat::check_result::CR_CONTINUE;
case l_undef:
TRACE("arith", tout << "check-lia giveup\n";);
if (ctx.get_config().m_arith_ignore_int)
return sat::check_result::CR_GIVEUP;
int_undef = true;
st = sat::check_result::CR_CONTINUE;
break;
}
@ -1045,6 +1011,8 @@ namespace arith {
}
if (!check_delayed_eqs())
return sat::check_result::CR_CONTINUE;
if (ctx.get_config().m_arith_ignore_int && int_undef)
return sat::check_result::CR_GIVEUP;
if (m_not_handled != nullptr) {
TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";);
return sat::check_result::CR_GIVEUP;
@ -1114,12 +1082,11 @@ namespace arith {
bool solver::check_delayed_eqs() {
bool found_diseq = false;
if (m_delayed_eqs_qhead == m_delayed_eqs.size())
if (m_delayed_eqs.empty())
return true;
force_push();
ctx.push(value_trail<unsigned>(m_delayed_eqs_qhead));
for (; m_delayed_eqs_qhead < m_delayed_eqs.size(); ++ m_delayed_eqs_qhead) {
auto p = m_delayed_eqs[m_delayed_eqs_qhead];
for (unsigned i = 0; i < m_delayed_eqs.size(); ++i) {
auto p = m_delayed_eqs[i];
auto const& e = p.first;
if (p.second)
new_eq_eh(e);
@ -1207,7 +1174,7 @@ namespace arith {
for (auto const& c : core)
m_core2.push_back(~c);
m_core2.push_back(lit);
add_clause(m_core2, pma);
add_redundant(m_core2, pma);
}
else {
auto* jst = euf::th_explain::propagate(*this, core, eqs, lit, pma);
@ -1229,26 +1196,31 @@ namespace arith {
void solver::set_conflict_or_lemma(literal_vector const& core, bool is_conflict) {
reset_evidence();
m_core.append(core);
++m_num_conflicts;
++m_stats.m_conflicts;
for (auto ev : m_explanation)
set_evidence(ev.ci());
TRACE("arith",
tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n";
for (literal c : m_core) tout << literal2expr(c) << "\n";
for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";);
DEBUG_CODE(
if (is_conflict) {
for (literal c : m_core) VERIFY(s().value(c) == l_true);
for (auto p : m_eqs) VERIFY(p.first->get_root() == p.second->get_root());
});
for (auto const& eq : m_eqs)
m_core.push_back(ctx.mk_literal(m.mk_eq(eq.first->get_expr(), eq.second->get_expr())));
for (literal& c : m_core)
c.neg();
add_clause(m_core, explain(hint_type::farkas_h));
if (is_conflict) {
DEBUG_CODE(
for (literal c : m_core) VERIFY(s().value(c) == l_true);
for (auto p : m_eqs) VERIFY(p.first->get_root() == p.second->get_root()));
++m_num_conflicts;
++m_stats.m_conflicts;
auto* hint = explain_conflict(m_core, m_eqs);
ctx.set_conflict(euf::th_explain::conflict(*this, m_core, m_eqs, hint));
}
else {
for (auto const& eq : m_eqs)
m_core.push_back(ctx.mk_literal(m.mk_eq(eq.first->get_expr(), eq.second->get_expr())));
for (literal& c : m_core)
c.neg();
add_redundant(m_core, explain(hint_type::farkas_h));
}
}
bool solver::is_infeasible() const {

View file

@ -51,14 +51,15 @@ namespace arith {
enum class hint_type {
farkas_h,
bound_h,
implied_eq_h
implied_eq_h
};
struct arith_proof_hint : public euf::th_proof_hint {
hint_type m_ty;
unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail;
arith_proof_hint(hint_type t, unsigned lh, unsigned lt, unsigned eh, unsigned et):
m_ty(t), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {}
hint_type m_ty;
unsigned m_num_le;
unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail;
arith_proof_hint(hint_type t, unsigned num_le, unsigned lh, unsigned lt, unsigned eh, unsigned et):
m_ty(t), m_num_le(num_le), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {}
expr* get_hint(euf::solver& s) const override;
};
@ -66,13 +67,14 @@ namespace arith {
vector<std::pair<rational, literal>> m_literals;
svector<std::tuple<euf::enode*,euf::enode*,bool>> m_eqs;
hint_type m_ty;
unsigned m_num_le = 0;
unsigned m_lit_head = 0, m_lit_tail = 0, m_eq_head = 0, m_eq_tail = 0;
void reset() { m_lit_head = m_lit_tail; m_eq_head = m_eq_tail; }
void add(euf::enode* a, euf::enode* b, bool is_eq) {
if (m_eq_tail < m_eqs.size())
m_eqs[m_eq_tail] = std::tuple(a, b, is_eq);
else
m_eqs.push_back(std::tuple(a, b, is_eq));
if (m_eq_tail < m_eqs.size())
m_eqs[m_eq_tail] = { a, b, is_eq };
else
m_eqs.push_back({a, b, is_eq });
m_eq_tail++;
}
public:
@ -82,6 +84,7 @@ namespace arith {
m_ty = ty;
reset();
}
void set_num_le(unsigned n) { m_num_le = n; }
void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); }
void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); }
void add_lit(rational const& coeff, literal lit) {
@ -94,7 +97,7 @@ namespace arith {
std::pair<rational, literal> const& lit(unsigned i) const { return m_literals[i]; }
std::tuple<enode*, enode*, bool> const& eq(unsigned i) const { return m_eqs[i]; }
arith_proof_hint* mk(euf::solver& s) {
return new (s.get_region()) arith_proof_hint(m_ty, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail);
return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail);
}
};
@ -105,11 +108,8 @@ namespace arith {
struct scope {
unsigned m_bounds_lim;
unsigned m_idiv_lim;
unsigned m_asserted_qhead;
unsigned m_asserted_lim;
unsigned m_underspecified_lim;
expr* m_not_handled;
};
class resource_limit : public lp::lp_resource_limit {
@ -218,10 +218,9 @@ namespace arith {
svector<euf::enode_pair> m_equalities; // asserted rows corresponding to equalities.
svector<theory_var> m_definitions; // asserted rows corresponding to definitions
svector<std::pair<euf::th_eq, bool>> m_delayed_eqs;
unsigned m_delayed_eqs_qhead = 0;
literal_vector m_asserted;
expr* m_not_handled{ nullptr };
expr* m_not_handled = nullptr;
ptr_vector<app> m_underspecified;
ptr_vector<expr> m_idiv_terms;
vector<ptr_vector<api_bound> > m_use_list; // bounds where variables are used.
@ -326,6 +325,7 @@ namespace arith {
void mk_bound_axiom(api_bound& b1, api_bound& b2);
void mk_power0_axioms(app* t, app* n);
void flush_bound_axioms();
void add_farkas_clause(sat::literal l1, sat::literal l2);
// bounds
struct compare_bounds {
@ -476,8 +476,10 @@ namespace arith {
arith_proof_hint_builder m_arith_hint;
arith_proof_hint const* explain(hint_type ty, sat::literal lit = sat::null_literal);
arith_proof_hint const* explain_implied_eq(euf::enode* a, euf::enode* b);
void explain_assumptions();
arith_proof_hint const* explain_implied_eq(lp::explanation const& e, euf::enode* a, euf::enode* b);
arith_proof_hint const* explain_trichotomy(sat::literal le, sat::literal ge, sat::literal eq);
arith_proof_hint const* explain_conflict(sat::literal_vector const& core, euf::enode_pair_vector const& eqs);
void explain_assumptions(lp::explanation const& e);
public:
@ -503,8 +505,8 @@ namespace arith {
void finalize_model(model& mdl) override { DEBUG_CODE(dbg_finalize_model(mdl);); }
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
bool add_dep(euf::enode* n, top_sort<euf::enode>& dep) override;
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
void eq_internalized(euf::enode* n) override;
void apply_sort_cnstr(euf::enode* n, sort* s) override {}
bool is_shared(theory_var v) const override;

View file

@ -18,7 +18,7 @@ Notes:
The module assumes a limited repertoire of arithmetic proof rules.
- farkas - inequalities, equalities and disequalities with coefficients
- implied-eq - last literal is a disequality. The literals before imply the corresponding equality.
- implied-eq - last literal is a disequality. The literals before imply the complementary equality.
- bound - last literal is a bound. It is implied by prior literals.
--*/
@ -26,13 +26,15 @@ The module assumes a limited repertoire of arithmetic proof rules.
#include "util/obj_pair_set.h"
#include "ast/ast_trail.h"
#include "ast/ast_util.h"
#include "ast/arith_decl_plugin.h"
#include "sat/smt/euf_proof_checker.h"
#include <iostream>
namespace arith {
class proof_checker : public euf::proof_checker_plugin {
class theory_checker : public euf::theory_checker_plugin {
struct row {
obj_map<expr, rational> m_coeffs;
rational m_coeff;
@ -49,8 +51,6 @@ namespace arith {
row m_ineq;
row m_conseq;
vector<row> m_eqs;
vector<row> m_ineqs;
vector<row> m_diseqs;
symbol m_farkas;
symbol m_implied_eq;
symbol m_bound;
@ -143,17 +143,13 @@ namespace arith {
SASSERT(m_todo.empty());
m_todo.push_back({ mul, e });
rational coeff1;
expr* e1, *e2, *e3;
expr* e1, *e2;
for (unsigned i = 0; i < m_todo.size(); ++i) {
auto [coeff, e] = m_todo[i];
if (a.is_mul(e, e1, e2) && a.is_numeral(e1, coeff1))
if (a.is_mul(e, e1, e2) && is_numeral(e1, coeff1))
m_todo.push_back({coeff*coeff1, e2});
else if (a.is_mul(e, e1, e2) && a.is_uminus(e1, e3) && a.is_numeral(e3, coeff1))
m_todo.push_back({-coeff*coeff1, e2});
else if (a.is_mul(e, e1, e2) && a.is_uminus(e2, e3) && a.is_numeral(e3, coeff1))
m_todo.push_back({ -coeff * coeff1, e1 });
else if (a.is_mul(e, e1, e2) && a.is_numeral(e2, coeff1))
m_todo.push_back({coeff*coeff1, e1});
else if (a.is_mul(e, e1, e2) && is_numeral(e2, coeff1))
m_todo.push_back({ coeff * coeff1, e1 });
else if (a.is_add(e))
for (expr* arg : *to_app(e))
m_todo.push_back({coeff, arg});
@ -163,15 +159,21 @@ namespace arith {
m_todo.push_back({coeff, e1});
m_todo.push_back({-coeff, e2});
}
else if (a.is_numeral(e, coeff1))
else if (is_numeral(e, coeff1))
r.m_coeff += coeff*coeff1;
else if (a.is_uminus(e, e1) && a.is_numeral(e1, coeff1))
r.m_coeff -= coeff*coeff1;
else
add(r, e, coeff);
}
m_todo.reset();
}
bool is_numeral(expr* e, rational& n) {
if (a.is_numeral(e, n))
return true;
if (a.is_uminus(e, e) && a.is_numeral(e, n))
return n.neg(), true;
return false;
}
bool check_ineq(row& r) {
if (r.m_coeffs.empty() && r.m_coeff > 0)
@ -182,9 +184,12 @@ namespace arith {
}
// triangulate equalities, substitute results into m_ineq, m_conseq.
void reduce_eq() {
// check consistency of equalities (they may be inconsisent)
bool reduce_eq() {
for (unsigned i = 0; i < m_eqs.size(); ++i) {
auto& r = m_eqs[i];
if (r.m_coeffs.empty() && r.m_coeff != 0)
return false;
if (r.m_coeffs.empty())
continue;
auto [v, coeff] = *r.m_coeffs.begin();
@ -193,6 +198,7 @@ namespace arith {
resolve(v, m_ineq, coeff, r);
resolve(v, m_conseq, coeff, r);
}
return true;
}
@ -231,10 +237,11 @@ namespace arith {
bool check_farkas() {
if (check_ineq(m_ineq))
return true;
reduce_eq();
if (!reduce_eq())
return true;
if (check_ineq(m_ineq))
return true;
IF_VERBOSE(3, display_row(verbose_stream() << "Failed to verify Farkas with reduced row ", m_ineq) << "\n");
// convert to expression, maybe follows from a cut.
return false;
}
@ -244,7 +251,8 @@ namespace arith {
// after all inequalities in ineq have been added up
//
bool check_bound() {
reduce_eq();
if (!reduce_eq())
return true;
if (check_ineq(m_conseq))
return true;
if (m_ineq.m_coeffs.empty() ||
@ -261,26 +269,6 @@ namespace arith {
return false;
}
//
// checking disequalities is TBD.
// it has to select only a subset of bounds to justify each inequality.
// example
// c <= x <= c, c <= y <= c => x = y
// for the proof of x <= y use the inequalities x <= c <= y
// for the proof of y <= x use the inequalities y <= c <= x
// example
// x <= y, y <= z, z <= u, u <= x => x = z
// for the proof of x <= z use the inequalities x <= y, y <= z
// for the proof of z <= x use the inequalities z <= u, u <= x
//
// so when m_diseqs is non-empty we can't just add inequalities with Farkas coefficients
// into m_ineq, since coefficients of the usable subset vanish.
//
bool check_diseq() {
return false;
}
std::ostream& display_row(std::ostream& out, row const& r) {
bool first = true;
for (auto const& [v, coeff] : r.m_coeffs) {
@ -316,29 +304,22 @@ namespace arith {
}
public:
proof_checker(ast_manager& m):
theory_checker(ast_manager& m):
m(m),
a(m),
m_farkas("farkas"),
m_implied_eq("implied-eq"),
m_bound("bound") {}
~proof_checker() override {}
void reset() {
m_ineq.reset();
m_conseq.reset();
m_eqs.reset();
m_ineqs.reset();
m_diseqs.reset();
m_strict = false;
}
bool add_ineq(rational const& coeff, expr* e, bool sign) {
if (!m_diseqs.empty())
return add_literal(fresh(m_ineqs), abs(coeff), e, sign);
else
return add_literal(m_ineq, abs(coeff), e, sign);
return add_literal(m_ineq, abs(coeff), e, sign);
}
bool add_conseq(rational const& coeff, expr* e, bool sign) {
@ -350,20 +331,12 @@ namespace arith {
linearize(r, rational(1), a);
linearize(r, rational(-1), b);
}
void add_diseq(expr* a, expr* b) {
row& r = fresh(m_diseqs);
linearize(r, rational(1), a);
linearize(r, rational(-1), b);
}
bool check() {
if (!m_diseqs.empty())
return check_diseq();
else if (!m_conseq.m_coeffs.empty())
return check_bound();
else
if (m_conseq.m_coeffs.empty())
return check_farkas();
else
return check_bound();
}
std::ostream& display(std::ostream& out) {
@ -375,44 +348,114 @@ namespace arith {
return out;
}
bool check(expr_ref_vector const& clause, app* jst, expr_ref_vector& units) override {
reset();
expr_mark pos, neg;
for (expr* e : clause)
if (m.is_not(e, e))
neg.mark(e, true);
else
pos.mark(e, true);
expr_ref_vector clause(app* jst) override {
expr_ref_vector result(m);
for (expr* arg : *jst)
if (m.is_bool(arg))
result.push_back(mk_not(m, arg));
return result;
}
if (jst->get_name() != m_farkas &&
jst->get_name() != m_bound &&
jst->get_name() != m_implied_eq) {
/**
Add implied equality as an inequality
*/
bool add_implied_ineq(bool sign, app* jst) {
unsigned n = jst->get_num_args();
if (n < 2)
return false;
expr* arg1 = jst->get_arg(n - 2);
expr* arg2 = jst->get_arg(n - 1);
rational coeff;
if (!a.is_numeral(arg1, coeff))
return false;
if (!m.is_not(arg2, arg2))
return false;
if (!m.is_eq(arg2, arg1, arg2))
return false;
if (!sign)
coeff.neg();
auto& r = m_ineq;
linearize(r, coeff, arg1);
linearize(r, -coeff, arg2);
m_strict = true;
return true;
}
bool check(app* jst) override {
reset();
bool is_bound = jst->get_name() == m_bound;
bool is_implied_eq = jst->get_name() == m_implied_eq;
bool is_farkas = jst->get_name() == m_farkas;
if (!is_farkas && !is_bound && !is_implied_eq) {
IF_VERBOSE(0, verbose_stream() << "unhandled inference " << mk_pp(jst, m) << "\n");
return false;
}
bool is_bound = jst->get_name() == m_bound;
bool even = true;
rational coeff;
expr* x, * y;
unsigned j = 0;
unsigned j = 0, num_le = 0;
for (expr* arg : *jst) {
if (even) {
if (!a.is_numeral(arg, coeff)) {
IF_VERBOSE(0, verbose_stream() << "not numeral " << mk_pp(jst, m) << "\n");
return false;
}
if (is_implied_eq) {
is_implied_eq = false;
if (!coeff.is_unsigned()) {
IF_VERBOSE(0, verbose_stream() << "not unsigned " << mk_pp(jst, m) << "\n");
return false;
}
num_le = coeff.get_unsigned();
if (!add_implied_ineq(false, jst)) {
IF_VERBOSE(0, display(verbose_stream() << "did not add implied eq"));
return false;
}
++j;
continue;
}
}
else {
bool sign = m.is_not(arg, arg);
if (a.is_le(arg) || a.is_lt(arg) || a.is_ge(arg) || a.is_gt(arg)) {
if (is_bound && j + 1 == jst->get_num_args())
add_conseq(coeff, arg, sign);
else if (num_le > 0) {
add_ineq(coeff, arg, sign);
--num_le;
if (num_le == 0) {
// we processed all the first inequalities,
// check that they imply one half of the implied equality.
if (!check()) {
// we might have added the wrong direction of the implied equality.
// so try the opposite inequality.
add_implied_ineq(true, jst);
add_implied_ineq(true, jst);
if (check()) {
reset();
add_implied_ineq(false, jst);
}
else {
IF_VERBOSE(0, display(verbose_stream() << "failed to check implied eq "));
return false;
}
}
else {
reset();
VERIFY(add_implied_ineq(true, jst));
}
}
}
else
add_ineq(coeff, arg, sign);
}
else if (m.is_eq(arg, x, y)) {
if (sign)
add_diseq(x, y);
if (is_bound && j + 1 == jst->get_num_args())
add_conseq(coeff, arg, sign);
else if (sign)
return check(); // it should be an implied equality
else
add_eq(x, y);
}
@ -420,27 +463,14 @@ namespace arith {
IF_VERBOSE(0, verbose_stream() << "not a recognized arithmetical relation " << mk_pp(arg, m) << "\n");
return false;
}
if (sign && !pos.is_marked(arg)) {
units.push_back(m.mk_not(arg));
pos.mark(arg, false);
}
else if (!sign && !neg.is_marked(arg)) {
units.push_back(arg);
neg.mark(arg, false);
}
}
even = !even;
++j;
}
if (check())
return true;
IF_VERBOSE(0, verbose_stream() << "did not check condition\n" << mk_pp(jst, m) << "\n"; display(verbose_stream()); );
return false;
return check();
}
void register_plugins(euf::proof_checker& pc) override {
void register_plugins(euf::theory_checker& pc) override {
pc.register_plugin(m_farkas, this);
pc.register_plugin(m_bound, this);
pc.register_plugin(m_implied_eq, this);

View file

@ -66,10 +66,6 @@ namespace array {
return assert_default(r);
case axiom_record::kind_t::is_extensionality:
return assert_extensionality(r.n->get_expr(), r.select->get_expr());
case axiom_record::kind_t::is_diff:
return assert_diff(r.n->get_app());
case axiom_record::kind_t::is_diffselect:
return assert_diff_select(r.n->get_app(), r.select->get_app());
case axiom_record::kind_t::is_congruence:
return assert_congruent_axiom(r.n->get_expr(), r.select->get_expr());
default:
@ -278,54 +274,6 @@ namespace array {
return add_clause(lit1, ~lit2);
}
/**
* a = b or default(a) != default(b) or a[md(a,b)] != b[md(a,b)]
*/
bool solver::assert_diff(expr* md) {
expr* x = nullptr, *y = nullptr;
VERIFY(a.is_maxdiff(md, x, y) || a.is_mindiff(md, x, y));
expr* args1[2] = { x, md };
expr* args2[2] = { y, md };
literal eq = eq_internalize(x, y);
literal eq_default = eq_internalize(a.mk_default(x), a.mk_default(y));
literal eq_md = eq_internalize(a.mk_select(2, args1), a.mk_select(2, args2));
return add_clause(eq, ~eq_default, ~eq_md);
}
/**
* a = b and a[i] != c[i] => i <= md(b, c) or default(b) != default(c)
* a = c and a[i] != b[i] => i <= md(b, c) or default(b) != default(c)
* where ai = a[i], md = md(b, c)
*/
bool solver::assert_diff_select(app* md, app* ai) {
SASSERT(a.is_select(ai));
SASSERT(ai->get_num_args() == 2);
expr* A = ai->get_arg(0);
expr* i = ai->get_arg(1);
expr* B = md->get_arg(0);
expr* C = md->get_arg(1);
literal eq_default = eq_internalize(a.mk_default(B), a.mk_default(C));
arith_util autil(m);
literal ineq = mk_literal(a.is_maxdiff(md) ? autil.mk_le(i, md) : autil.mk_le(md, i));
bool is_new = false;
if (ctx.get_enode(A)->get_root() == ctx.get_enode(B)->get_root()) {
literal eq_ab = eq_internalize(A, B);
expr* args[2] = { C, i };
literal eq_select = eq_internalize(ai, a.mk_select(2, args));
if (add_clause(~eq_ab, eq_select, ineq, ~eq_default))
is_new = true;
}
if (ctx.get_enode(A)->get_root() == ctx.get_enode(C)->get_root()) {
literal eq_ac = eq_internalize(A, C);
expr* args[2] = { B, i };
literal eq_select = eq_internalize(ai, a.mk_select(2, args));
if (add_clause(~eq_ac, eq_select, ineq, ~eq_default))
is_new = true;
}
return is_new;
}
bool solver::is_map_combinator(expr* map) const {
return a.is_map(map) || a.is_union(map) || a.is_intersect(map) || a.is_difference(map) || a.is_complement(map);
}
@ -625,6 +573,10 @@ namespace array {
return change;
}
/**
* For every occurrence of as-array(f) and every occurrence of f(t)
* add equality select(as-array(f), t) = f(t)
*/
bool solver::add_as_array_eqs(euf::enode* n) {
func_decl* f = nullptr;
bool change = false;
@ -734,26 +686,5 @@ namespace array {
return false;
}
bool solver::add_diff_select_axioms() {
bool added = false;
auto add_diff_select = [&](euf::enode* md, euf::enode* a) {
var_data const& d = get_var_data(find(get_th_var(a)));
for (euf::enode* select : d.m_parent_selects) {
if (assert_diff_select(md->get_app(), select->get_app()))
added = true;
}
};
for (euf::enode* md : m_minmaxdiffs) {
euf::enode* a = md->get_arg(0);
euf::enode* b = md->get_arg(1);
add_diff_select(md, a);
add_diff_select(md, b);
}
return added;
}
}

View file

@ -20,9 +20,9 @@ Author:
namespace array {
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant)) {
if (!visit_rec(m, e, sign, root)) {
TRACE("array", tout << mk_pp(e, m) << "\n";);
return sat::null_literal;
}
@ -32,8 +32,8 @@ namespace array {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
visit_rec(m, e, false, false, redundant);
void solver::internalize(expr* e) {
visit_rec(m, e, false, false);
}
euf::theory_var solver::mk_var(euf::enode* n) {
@ -66,7 +66,7 @@ namespace array {
if (visited(e))
return true;
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
ctx.internalize(e);
euf::enode* n = expr2enode(e);
ensure_var(n);
return true;
@ -115,14 +115,8 @@ namespace array {
SASSERT(is_array(n->get_arg(0)));
push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1)));
break;
case OP_ARRAY_MINDIFF:
case OP_ARRAY_MAXDIFF:
push_axiom(diff_axiom(n));
m_minmaxdiffs.push_back(n);
ctx.push(push_back_vector(m_minmaxdiffs));
break;
case OP_ARRAY_DEFAULT:
add_parent_default(find(n->get_arg(0)), n);
add_parent_default(find(n->get_arg(0)));
break;
case OP_ARRAY_MAP:
case OP_SET_UNION:
@ -176,10 +170,6 @@ namespace array {
break;
case OP_ARRAY_EXT:
break;
case OP_ARRAY_MINDIFF:
case OP_ARRAY_MAXDIFF:
// todo
break;
case OP_ARRAY_DEFAULT:
set_prop_upward(find(n->get_arg(0)));
break;

View file

@ -69,14 +69,21 @@ namespace array {
values.set(n->get_expr_id(), n->get_expr());
return;
}
theory_var v = get_th_var(n);
euf::enode* d = get_default(v);
if (a.is_const(n->get_expr())) {
expr* val = values.get(d->get_root_id());
SASSERT(val);
values.set(n->get_expr_id(), a.mk_const_array(n->get_sort(), val));
return;
}
unsigned arity = get_array_arity(srt);
func_decl * f = mk_aux_decl_for_array_sort(m, srt);
func_interp * fi = alloc(func_interp, m, arity);
mdl.register_decl(f, fi);
theory_var v = get_th_var(n);
euf::enode* d = get_default(v);
if (d && !fi->get_else())
fi->set_else(values.get(d->get_root_id()));

View file

@ -101,9 +101,6 @@ namespace array {
else if (!turn[idx] && add_interface_equalities())
return sat::check_result::CR_CONTINUE;
}
if (add_diff_select_axioms())
return sat::check_result::CR_CONTINUE;
if (m_delay_qhead < m_axiom_trail.size())
return sat::check_result::CR_CONTINUE;
@ -164,6 +161,10 @@ namespace array {
auto& d2 = get_var_data(v2);
if (d2.m_prop_upward && !d1.m_prop_upward)
set_prop_upward(v1);
if (d1.m_has_default && !d2.m_has_default)
add_parent_default(v2);
if (!d1.m_has_default && d2.m_has_default)
add_parent_default(v1);
for (euf::enode* lambda : d2.m_lambdas)
add_lambda(v1, lambda);
for (euf::enode* lambda : d2.m_parent_lambdas)
@ -206,13 +207,13 @@ namespace array {
propagate_select_axioms(d, lambda);
}
void solver::add_parent_default(theory_var v, euf::enode* def) {
SASSERT(a.is_default(def->get_expr()));
void solver::add_parent_default(theory_var v) {
auto& d = get_var_data(find(v));
ctx.push(value_trail(d.m_has_default));
d.m_has_default = true;
for (euf::enode* lambda : d.m_lambdas)
push_axiom(default_axiom(lambda));
if (should_prop_upward(d))
propagate_parent_default(v);
propagate_parent_default(v);
}
void solver::propagate_select_axioms(var_data const& d, euf::enode* lambda) {
@ -250,7 +251,7 @@ namespace array {
return;
ctx.push(reset_flag_trail(d.m_prop_upward));
d.m_prop_upward = true;
if (should_prop_upward(d))
if (should_prop_upward(d))
propagate_parent_select_axioms(v);
set_prop_upward(d);
}

View file

@ -50,7 +50,8 @@ namespace array {
// void log_drat(array_justification const& c);
struct var_data {
bool m_prop_upward{ false };
bool m_prop_upward = false ;
bool m_has_default = false;
euf::enode_vector m_lambdas; // equivalent nodes that have beta reduction properties
euf::enode_vector m_parent_lambdas; // parents that have beta reduction properties
euf::enode_vector m_parent_selects; // parents that use array in select position
@ -83,8 +84,6 @@ namespace array {
is_store,
is_select,
is_extensionality,
is_diff,
is_diffselect,
is_default,
is_congruence
};
@ -94,7 +93,7 @@ namespace array {
is_applied
};
kind_t m_kind;
state_t m_state { state_t::is_new };
state_t m_state = state_t::is_new;
euf::enode* n;
euf::enode* select;
axiom_record(kind_t k, euf::enode* n, euf::enode* select = nullptr) : m_kind(k), n(n), select(select) {}
@ -165,9 +164,6 @@ namespace array {
axiom_record store_axiom(euf::enode* n) { return axiom_record(axiom_record::kind_t::is_store, n); }
axiom_record extensionality_axiom(euf::enode* x, euf::enode* y) { return axiom_record(axiom_record::kind_t::is_extensionality, x, y); }
axiom_record congruence_axiom(euf::enode* a, euf::enode* b) { return axiom_record(axiom_record::kind_t::is_congruence, a, b); }
axiom_record diff_axiom(euf::enode* md) { return axiom_record(axiom_record::kind_t::is_diff, md); }
euf::enode_vector m_minmaxdiffs;
axiom_record diff_select_axiom(euf::enode* md, euf::enode* ai) { return axiom_record(axiom_record::kind_t::is_diffselect, md, ai); }
scoped_ptr<sat::constraint_base> m_constraint;
@ -180,15 +176,12 @@ namespace array {
bool assert_select_map_axiom(app* select, app* map);
bool assert_select_lambda_axiom(app* select, expr* lambda);
bool assert_extensionality(expr* e1, expr* e2);
bool assert_diff(expr* md);
bool assert_diff_select(app* ai, app* md);
bool assert_default_map_axiom(app* map);
bool assert_default_const_axiom(app* cnst);
bool assert_default_store_axiom(app* store);
bool assert_congruent_axiom(expr* e1, expr* e2);
bool add_delayed_axioms();
bool add_as_array_eqs(euf::enode* n);
bool add_diff_select_axioms();
expr_ref apply_map(app* map, unsigned n, expr* const* args);
bool is_map_combinator(expr* e) const;
@ -202,7 +195,7 @@ namespace array {
// solving
void add_parent_select(theory_var v_child, euf::enode* select);
void add_parent_default(theory_var v_child, euf::enode* def);
void add_parent_default(theory_var v_child);
void add_lambda(theory_var v, euf::enode* lambda);
void add_parent_lambda(theory_var v_child, euf::enode* lambda);
@ -295,8 +288,8 @@ namespace array {
bool include_func_interp(func_decl* f) const override { return a.is_ext(f); }
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
bool add_dep(euf::enode* n, top_sort<euf::enode>& dep) override;
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
euf::theory_var mk_var(euf::enode* n) override;
void apply_sort_cnstr(euf::enode* n, sort* s) override;
bool is_shared(theory_var v) const override;

View file

@ -101,10 +101,10 @@ namespace bv {
get_var(n);
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
force_push();
SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant))
if (!visit_rec(m, e, sign, root))
return sat::null_literal;
sat::literal lit = expr2literal(e);
if (sign)
@ -112,14 +112,14 @@ namespace bv {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
void solver::internalize(expr* e) {
force_push();
visit_rec(m, e, false, false, redundant);
visit_rec(m, e, false, false);
}
bool solver::visit(expr* e) {
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
ctx.internalize(e);
return true;
}
m_stack.push_back(sat::eframe(e));
@ -254,7 +254,7 @@ namespace bv {
for (unsigned i = 0; i < bv_size; i++) {
expr_ref b2b(bv.mk_bit2bool(e, i), m);
m_bits[v].push_back(sat::null_literal);
sat::literal lit = ctx.internalize(b2b, false, false, m_is_redundant);
sat::literal lit = ctx.internalize(b2b, false, false);
TRACE("bv", tout << "add-bit: " << lit << " " << literal2expr(lit) << "\n";);
if (m_bits[v].back() == sat::null_literal)
m_bits[v].back() = lit;
@ -352,7 +352,7 @@ namespace bv {
SASSERT(bits.size() == m_bits[v].size());
unsigned i = 0;
for (expr* bit : bits) {
sat::literal lit = ctx.internalize(bit, false, false, m_is_redundant);
sat::literal lit = ctx.internalize(bit, false, false);
TRACE("bv", tout << "set " << m_bits[v][i] << " == " << lit << "\n";);
add_equiv(lit, m_bits[v][i]);
ctx.add_aux_equiv(lit, m_bits[v][i]);
@ -361,7 +361,7 @@ namespace bv {
return;
}
for (expr* bit : bits)
add_bit(v, ctx.internalize(bit, false, false, m_is_redundant));
add_bit(v, ctx.internalize(bit, false, false));
for (expr* bit : bits)
get_var(expr2enode(bit));
SASSERT(get_bv_size(n) == bits.size());
@ -379,7 +379,7 @@ namespace bv {
sat::literal solver::mk_true() {
if (m_true == sat::null_literal) {
ctx.push(value_trail<sat::literal>(m_true));
m_true = ctx.internalize(m.mk_true(), false, true, false);
m_true = ctx.internalize(m.mk_true(), false, true);
s().assign_unit(m_true);
}
return m_true;
@ -501,7 +501,7 @@ namespace bv {
m_bb.mk_sle(arg1_bits.size(), arg1_bits.data(), arg2_bits.data(), le);
else
m_bb.mk_ule(arg1_bits.size(), arg1_bits.data(), arg2_bits.data(), le);
literal def = ctx.internalize(le, false, false, m_is_redundant);
literal def = ctx.internalize(le, false, false);
if (Negated)
def.neg();
add_def(def, expr2literal(n));
@ -606,7 +606,7 @@ namespace bv {
get_arg_bits(n, 1, arg2_bits);
expr_ref out(m);
fn(arg1_bits.size(), arg1_bits.data(), arg2_bits.data(), out);
sat::literal def = ctx.internalize(out, false, false, m_is_redundant);
sat::literal def = ctx.internalize(out, false, false);
add_def(def, expr2literal(n));
}
@ -762,12 +762,11 @@ namespace bv {
return;
if (v1 > v2)
std::swap(v1, v2);
flet<bool> _red(m_is_redundant, true);
++m_stats.m_ackerman;
expr* o1 = var2expr(v1);
expr* o2 = var2expr(v2);
expr_ref oe = mk_var_eq(v1, v2);
literal oeq = ctx.internalize(oe, false, false, m_is_redundant);
literal oeq = ctx.internalize(oe, false, false);
unsigned sz = m_bits[v1].size();
TRACE("bv", tout << "ackerman-eq: " << s().scope_lvl() << " " << oe << "\n";);
literal_vector eqs;
@ -781,6 +780,7 @@ namespace bv {
eqs.push_back(~eq);
}
TRACE("bv", for (auto l : eqs) tout << mk_bounded_pp(literal2expr(l), m) << " "; tout << "\n";);
s().add_clause(eqs.size(), eqs.data(), sat::status::th(m_is_redundant, get_id()));
euf::th_proof_hint* ph = ctx.mk_smt_clause(name(), eqs.size(), eqs.data());
s().mk_clause(eqs, sat::status::th(true, m.get_basic_family_id(), ph));
}
}

View file

@ -56,7 +56,7 @@ namespace bv {
m_ackerman(*this),
m_bb(m, get_config()),
m_find(*this) {
m_bb.set_flat(false);
m_bb.set_flat_and_or(false);
}
bool solver::is_fixed(euf::theory_var v, expr_ref& val, sat::literal_vector& lits) {
@ -169,7 +169,7 @@ namespace bv {
TRACE("bv", tout << "found new diseq axiom\n" << pp(v1) << pp(v2););
m_stats.m_num_diseq_static++;
expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m);
add_unit(~ctx.internalize(eq, false, false, m_is_redundant));
add_unit(~ctx.internalize(eq, false, false));
}
std::ostream& solver::display(std::ostream& out, theory_var v) const {
@ -316,7 +316,7 @@ namespace bv {
case bv_justification::kind_t::eq2bit:
SASSERT(s().value(c.m_antecedent) == l_true);
r.push_back(c.m_antecedent);
ctx.add_antecedent(var2enode(c.m_v1), var2enode(c.m_v2));
ctx.add_antecedent(probing, var2enode(c.m_v1), var2enode(c.m_v2));
break;
case bv_justification::kind_t::ne2bit: {
r.push_back(c.m_antecedent);
@ -384,8 +384,8 @@ namespace bv {
break;
}
case bv_justification::kind_t::bv2int: {
ctx.add_antecedent(c.a, c.b);
ctx.add_antecedent(c.a, c.c);
ctx.add_antecedent(probing, c.a, c.b);
ctx.add_antecedent(probing, c.a, c.c);
break;
}
}
@ -398,67 +398,123 @@ namespace bv {
sat::literal leq1(s().num_vars() + 1, false);
sat::literal leq2(s().num_vars() + 2, false);
expr_ref eq1(m), eq2(m);
expr* a1 = nullptr, *a2 = nullptr, *b1 = nullptr, *b2 = nullptr;
if (c.m_kind == bv_justification::kind_t::bv2int) {
eq1 = m.mk_eq(c.a->get_expr(), c.b->get_expr());
eq2 = m.mk_eq(c.a->get_expr(), c.c->get_expr());
ctx.set_tmp_bool_var(leq1.var(), eq1);
ctx.set_tmp_bool_var(leq2.var(), eq1);
a1 = c.a->get_expr();
a2 = c.b->get_expr();
b1 = c.a->get_expr();
b2 = c.c->get_expr();
}
else if (c.m_kind != bv_justification::kind_t::bit2ne) {
expr* e1 = var2expr(c.m_v1);
expr* e2 = var2expr(c.m_v2);
eq1 = m.mk_eq(e1, e2);
a1 = var2expr(c.m_v1);
a2 = var2expr(c.m_v2);
}
if (a1) {
eq1 = m.mk_eq(a1, a2);
ctx.set_tmp_bool_var(leq1.var(), eq1);
}
if (b1) {
eq2 = m.mk_eq(b1, b2);
ctx.set_tmp_bool_var(leq2.var(), eq2);
}
ctx.push(value_trail(m_lit_tail));
ctx.push(restore_vector(m_proof_literals));
sat::literal_vector lits;
switch (c.m_kind) {
case bv_justification::kind_t::eq2bit:
lits.push_back(~leq1);
lits.push_back(~c.m_antecedent);
lits.push_back(c.m_consequent);
m_proof_literals.append(lits);
lits.push_back(~leq1);
break;
case bv_justification::kind_t::ne2bit:
get_antecedents(c.m_consequent, c.to_index(), lits, true);
for (auto& lit : lits)
lit.neg();
lits.push_back(c.m_consequent);
m_proof_literals.append(lits);
break;
case bv_justification::kind_t::bit2eq:
get_antecedents(leq1, c.to_index(), lits, true);
for (auto& lit : lits)
lit.neg();
m_proof_literals.append(lits);
lits.push_back(leq1);
break;
case bv_justification::kind_t::bit2ne:
get_antecedents(c.m_consequent, c.to_index(), lits, true);
lits.push_back(~c.m_consequent);
for (auto& lit : lits)
lit.neg();
lits.push_back(c.m_consequent);
m_proof_literals.append(lits);
break;
case bv_justification::kind_t::bv2int:
get_antecedents(leq1, c.to_index(), lits, true);
get_antecedents(leq2, c.to_index(), lits, true);
for (auto& lit : lits)
lit.neg();
m_proof_literals.append(lits);
lits.push_back(leq1);
lits.push_back(leq2);
break;
}
ctx.get_drat().add(lits, status());
m_lit_head = m_lit_tail;
m_lit_tail = m_proof_literals.size();
proof_hint* ph = new (get_region()) proof_hint(c.m_kind, m_proof_literals, m_lit_head, m_lit_tail, a1, a2, b1, b2);
auto st = sat::status::th(false, m.get_basic_family_id(), ph);
ctx.get_drat().add(lits, st);
m_lit_head = m_lit_tail;
// TBD, a proper way would be to delete the lemma after use.
ctx.set_tmp_bool_var(leq1.var(), nullptr);
ctx.set_tmp_bool_var(leq2.var(), nullptr);
}
void solver::asserted(literal l) {
expr* solver::proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
sort* proof = m.mk_proof_sort();
expr_ref_vector& args = s.expr_args();
ptr_buffer<sort> sorts;
for (unsigned i = m_lit_head; i < m_lit_tail; ++i)
args.push_back(s.literal2expr(m_proof_literals[i]));
if (m_kind == bv_justification::kind_t::eq2bit)
args.push_back(m.mk_not(m.mk_eq(a1, a2)));
else if (a1)
args.push_back(m.mk_eq(a1, a2));
if (b1)
args.push_back(m.mk_eq(b1, b2));
for (auto * arg : args)
sorts.push_back(arg->get_sort());
symbol th;
switch (m_kind) {
case bv_justification::kind_t::eq2bit:
th = "eq2bit"; break;
case bv_justification::kind_t::ne2bit:
th = "ne2bit"; break;
case bv_justification::kind_t::bit2eq:
th = "bit2eq"; break;
case bv_justification::kind_t::bit2ne:
th = "bit2ne"; break;
case bv_justification::kind_t::bv2int:
th = "bv2int"; break;
}
func_decl* f = m.mk_func_decl(th, sorts.size(), sorts.data(), proof);
return m.mk_app(f, args);
};
void solver::asserted(literal l) {
atom* a = get_bv2a(l.var());
TRACE("bv", tout << "asserted: " << l << "\n";);
if (a) {
force_push();
m_prop_queue.push_back(propagation_item(a));
for (auto p : a->m_bit2occ)
del_eq_occurs(p.first, p.second);
del_eq_occurs(p.first, p.second);
}
}

View file

@ -36,6 +36,9 @@ namespace bv {
}
};
class solver : public euf::th_euf_solver {
typedef rational numeral;
typedef euf::theory_var theory_var;
@ -95,8 +98,19 @@ namespace bv {
sat::justification mk_ne2bit_justification(unsigned idx, theory_var v1, theory_var v2, sat::literal c, sat::literal a);
sat::ext_constraint_idx mk_bv2int_justification(theory_var v1, theory_var v2, euf::enode* a, euf::enode* b, euf::enode* c);
void log_drat(bv_justification const& c);
class proof_hint : public euf::th_proof_hint {
bv_justification::kind_t m_kind;
sat::literal_vector& m_proof_literals;
unsigned m_lit_head, m_lit_tail;
expr* a1 = nullptr, * a2 = nullptr, * b1 = nullptr, * b2 = nullptr;
public:
proof_hint(bv_justification::kind_t k, sat::literal_vector& pl, unsigned lh, unsigned lt, expr* a1 = nullptr, expr* a2 = nullptr, expr* b1 = nullptr, expr* b2 = nullptr) :
m_kind(k), m_proof_literals(pl), m_lit_head(lh), m_lit_tail(lt), a1(a1), a2(a2), b1(b1), b2(b2) {}
expr* get_hint(euf::solver& s) const override;
};
sat::literal_vector m_proof_literals;
unsigned m_lit_head = 0, m_lit_tail = 0;
/**
\brief Structure used to store the position of a bitvector variable that
contains the true_literal/false_literal.
@ -409,8 +423,8 @@ namespace bv {
std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override { return false; }
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override { return false; }
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
void eq_internalized(euf::enode* n) override;
euf::theory_var mk_var(euf::enode* n) override;
void apply_sort_cnstr(euf::enode * n, sort * s) override;

View file

@ -0,0 +1,75 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bv_theory_checker.cpp
Abstract:
Plugin for bitvector lemmas
Author:
Nikolaj Bjorner (nbjorner) 2022-08-28
Notes:
--*/
#include "sat/smt/euf_solver.h"
#include "sat/smt/bv_theory_checker.h"
namespace bv {
/**
bv is a generic rule used for internalizing bit-vectors.
It corresponds to the Tseitin of bit-vectors.
To bypass theory checking we pretend it is trusted.
*/
bool theory_checker::check_bv(app* jst) { return true; }
/**
Let x, y be bit-vector terms and k be an assignment to constants bit2eq encodes the rule:
x = k, y = k
------------
x = y
*/
bool theory_checker::check_bit2eq(app* jst) { return true; }
/**
x[i] = false, y[i] = true
-------------------------
x != y
*/
bool theory_checker::check_bit2ne(app* jst) { return true; }
/**
x = y
-----------
x[i] = y[i]
*/
bool theory_checker::check_eq2bit(app* jst) { return true; }
/**
x != y, x is assigned on all but position i, x[j] = y[j] on other positions.
----------------------------------------------------------------------------
x[i] != y[i]
*/
bool theory_checker::check_ne2bit(app* jst) { return true; }
/**
int2bv(bv2int(x)) = x when int2bv(bv2int(x)) has same sort as x
n = bv2int(x), n = z
--------------------
int2bv(z) = x
*/
bool theory_checker::check_bv2int(app* jst) { return true; }
}

View file

@ -0,0 +1,95 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bv_theory_checker.h
Abstract:
Plugin for bitvector lemmas
Author:
Nikolaj Bjorner (nbjorner) 2022-08-28
Notes:
--*/
#pragma once
#include "util/obj_pair_set.h"
#include "ast/ast_trail.h"
#include "ast/ast_util.h"
#include "ast/bv_decl_plugin.h"
#include "sat/smt/euf_proof_checker.h"
#include <iostream>
namespace bv {
class theory_checker : public euf::theory_checker_plugin {
ast_manager& m;
bv_util bv;
symbol m_eq2bit = symbol("eq2bit");
symbol m_ne2bit = symbol("ne2bit");
symbol m_bit2eq = symbol("bit2eq");
symbol m_bit2ne = symbol("bit2ne");
symbol m_bv2int = symbol("bv2int");
symbol m_bv = symbol("bv");
bool check_bv(app* jst);
bool check_bit2eq(app* jst);
bool check_bit2ne(app* jst);
bool check_eq2bit(app* jst);
bool check_ne2bit(app* jst);
bool check_bv2int(app* jst);
public:
theory_checker(ast_manager& m):
m(m),
bv(m) {}
bool check(app* jst) override {
if (jst->get_name() == m_bv)
return check_bv(jst);
if (jst->get_name() == m_eq2bit)
return check_eq2bit(jst);
if (jst->get_name() == m_ne2bit)
return check_ne2bit(jst);
if (jst->get_name() == m_bit2eq)
return check_bit2eq(jst);
if (jst->get_name() == m_bit2ne)
return check_bit2ne(jst);
if (jst->get_name() == m_bv2int)
return check_bv2int(jst);
return false;
}
expr_ref_vector clause(app* jst) override {
expr_ref_vector result(m);
if (jst->get_name() == m_bv) {
for (expr* arg : *jst)
result.push_back(mk_not(m, arg));
}
else {
for (expr* arg : *jst)
result.push_back(arg);
}
return result;
}
void register_plugins(euf::theory_checker& pc) override {
pc.register_plugin(m_bv, this);
pc.register_plugin(m_bit2eq, this);
pc.register_plugin(m_bit2ne, this);
pc.register_plugin(m_eq2bit, this);
pc.register_plugin(m_ne2bit, this);
pc.register_plugin(m_bv2int, this);
}
};
}

View file

@ -0,0 +1,50 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
distinct_proof_checker.h
Abstract:
Plugin for checking distinct internalization
Author:
Nikolaj Bjorner (nbjorner) 2022-10-07
Note:
First version just trusts that distinct is internalized correctly.
--*/
#pragma once
#include "sat/smt/euf_proof_checker.h"
#include <iostream>
namespace distinct {
class theory_checker : public euf::theory_checker_plugin {
ast_manager& m;
public:
theory_checker(ast_manager& m):
m(m) {
}
expr_ref_vector clause(app* jst) override {
expr_ref_vector result(m);
result.append(jst->get_num_args(), jst->get_args());
return result;
}
bool check(app* jst) override { return true; }
void register_plugins(euf::theory_checker& pc) override {
pc.register_plugin(symbol("alldiff"), this);
}
};
}

View file

@ -103,14 +103,15 @@ namespace dt {
*/
void solver::assert_eq_axiom(enode* n1, expr* e2, literal antecedent) {
expr* e1 = n1->get_expr();
euf::th_proof_hint* ph = ctx.mk_smt_prop_hint(name(), antecedent, e1, e2);
if (antecedent == sat::null_literal)
add_unit(eq_internalize(e1, e2));
add_unit(eq_internalize(e1, e2), ph);
else if (s().value(antecedent) == l_true) {
euf::enode* n2 = e_internalize(e2);
ctx.propagate(n1, n2, euf::th_explain::propagate(*this, antecedent, n1, n2));
ctx.propagate(n1, n2, euf::th_explain::propagate(*this, antecedent, n1, n2, ph));
}
else
add_clause(~antecedent, eq_internalize(e1, e2));
add_clause(~antecedent, eq_internalize(e1, e2), ph);
}
/**
@ -162,7 +163,8 @@ namespace dt {
literal l = ctx.enode2literal(r);
SASSERT(s().value(l) == l_false);
clear_mark();
ctx.set_conflict(euf::th_explain::conflict(*this, ~l, c, r->get_arg(0)));
auto* ph = ctx.mk_smt_hint(name(), ~l, c, r->get_arg(0));
ctx.set_conflict(euf::th_explain::conflict(*this, ~l, c, r->get_arg(0), ph));
}
/**
@ -200,7 +202,9 @@ namespace dt {
// update_field is identity if 'n' is not created by a matching constructor.
assert_eq_axiom(n, arg1, ~is_con);
app_ref n_is_con(m.mk_app(rec, own), m);
add_clause(~is_con, mk_literal(n_is_con));
literal _n_is_con = mk_literal(n_is_con);
auto* ph = ctx.mk_smt_hint(name(), is_con, ~_n_is_con);
add_clause(~is_con, _n_is_con, ph);
}
euf::theory_var solver::mk_var(enode* n) {
@ -309,7 +313,8 @@ namespace dt {
}
}
}
ctx.set_conflict(euf::th_explain::conflict(*this, m_lits));
auto* ph = ctx.mk_smt_hint(name(), m_lits);
ctx.set_conflict(euf::th_explain::conflict(*this, m_lits, ph));
}
/**
@ -445,8 +450,10 @@ namespace dt {
++idx;
}
TRACE("dt", tout << "propagate " << num_unassigned << " eqs: " << eqs.size() << "\n";);
if (num_unassigned == 0)
ctx.set_conflict(euf::th_explain::conflict(*this, m_lits, eqs));
if (num_unassigned == 0) {
auto* ph = ctx.mk_smt_hint(name(), m_lits, eqs);
ctx.set_conflict(euf::th_explain::conflict(*this, m_lits, eqs, ph));
}
else if (num_unassigned == 1) {
// propagate remaining recognizer
SASSERT(!m_lits.empty());
@ -460,7 +467,13 @@ namespace dt {
app_ref rec_app(m.mk_app(rec, n->get_expr()), m);
consequent = mk_literal(rec_app);
}
ctx.propagate(consequent, euf::th_explain::propagate(*this, m_lits, eqs, consequent));
euf::th_proof_hint* ph = nullptr;
if (ctx.use_drat()) {
m_lits.push_back(~consequent);
ph = ctx.mk_smt_hint(name(), m_lits, eqs);
m_lits.pop_back();
}
ctx.propagate(consequent, euf::th_explain::propagate(*this, m_lits, eqs, consequent, ph));
}
else if (get_config().m_dt_lazy_splits == 0 || (!srt->is_infinite() && get_config().m_dt_lazy_splits == 1))
// there are more than 2 unassigned recognizers...
@ -477,7 +490,7 @@ namespace dt {
auto* con2 = d2->m_constructor;
TRACE("dt", tout << "merging v" << v1 << " v" << v2 << "\n" << ctx.bpp(var2enode(v1)) << " == " << ctx.bpp(var2enode(v2)) << " " << ctx.bpp(con1) << " " << ctx.bpp(con2) << "\n";);
if (con1 && con2 && con1->get_decl() != con2->get_decl())
ctx.set_conflict(euf::th_explain::conflict(*this, con1, con2));
ctx.set_conflict(euf::th_explain::conflict(*this, con1, con2, ctx.mk_smt_hint(name(), con1, con2)));
else if (con2 && !con1) {
ctx.push(set_ptr_trail<enode>(d1->m_constructor));
// check whether there is a recognizer in d1 that conflicts with con2;
@ -702,7 +715,7 @@ namespace dt {
if (res) {
clear_mark();
ctx.set_conflict(euf::th_explain::conflict(*this, m_used_eqs));
ctx.set_conflict(euf::th_explain::conflict(*this, m_used_eqs, ctx.mk_smt_hint(name(), m_used_eqs)));
TRACE("dt", tout << "occurs check conflict: " << ctx.bpp(n) << "\n";);
}
return res;
@ -791,8 +804,8 @@ namespace dt {
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
if (!visit_rec(m, e, sign, root, redundant))
sat::literal solver::internalize(expr* e, bool sign, bool root) {
if (!visit_rec(m, e, sign, root))
return sat::null_literal;
auto lit = ctx.expr2literal(e);
if (sign)
@ -800,15 +813,15 @@ namespace dt {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
visit_rec(m, e, false, false, redundant);
void solver::internalize(expr* e) {
visit_rec(m, e, false, false);
}
bool solver::visit(expr* e) {
if (visited(e))
return true;
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
ctx.internalize(e);
if (is_datatype(e))
mk_var(expr2enode(e));
return true;

View file

@ -154,8 +154,8 @@ namespace dt {
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
bool add_dep(euf::enode* n, top_sort<euf::enode>& dep) override;
bool include_func_interp(func_decl* f) const override;
sat::literal internalize(expr* e, bool sign, bool root, bool redundant) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
euf::theory_var mk_var(euf::enode* n) override;
void apply_sort_cnstr(euf::enode* n, sort* s) override;
bool is_shared(theory_var v) const override { return false; }

View file

@ -20,7 +20,7 @@ Author:
namespace euf {
ackerman::ackerman(solver& s, ast_manager& m): s(s), m(m) {
ackerman::ackerman(solver& ctx, ast_manager& m): ctx(ctx), m(m) {
new_tmp();
}
@ -74,14 +74,7 @@ namespace euf {
m.inc_ref(inf->c);
new_tmp();
}
other->m_count++;
if (other->m_count > m_high_watermark) {
if (other->is_cc)
add_cc(other->a, other->b);
else
add_eq(other->a, other->b, other->c);
other->m_count = 0;
}
other->m_count++;
inference::push_to_front(m_queue, other);
}
@ -100,31 +93,31 @@ namespace euf {
}
bool ackerman::enable_cc(app* a, app* b) {
if (!s.enable_ackerman_axioms(a))
if (!ctx.enable_ackerman_axioms(a))
return false;
if (!s.enable_ackerman_axioms(b))
if (!ctx.enable_ackerman_axioms(b))
return false;
for (expr* arg : *a)
if (!s.enable_ackerman_axioms(arg))
if (!ctx.enable_ackerman_axioms(arg))
return false;
for (expr* arg : *b)
if (!s.enable_ackerman_axioms(arg))
if (!ctx.enable_ackerman_axioms(arg))
return false;
return true;
}
bool ackerman::enable_eq(expr* a, expr* b, expr* c) {
return s.enable_ackerman_axioms(a) &&
s.enable_ackerman_axioms(b) &&
s.enable_ackerman_axioms(c);
return ctx.enable_ackerman_axioms(a) &&
ctx.enable_ackerman_axioms(b) &&
ctx.enable_ackerman_axioms(c);
}
void ackerman::cg_conflict_eh(expr * n1, expr * n2) {
if (!is_app(n1) || !is_app(n2))
return;
if (!s.enable_ackerman_axioms(n1))
if (!ctx.enable_ackerman_axioms(n1))
return;
SASSERT(!s.m_drating);
SASSERT(!ctx.m_drating);
app* a = to_app(n1);
app* b = to_app(n2);
if (a->get_decl() != b->get_decl() || a->get_num_args() != b->get_num_args())
@ -139,7 +132,7 @@ namespace euf {
void ackerman::used_eq_eh(expr* a, expr* b, expr* c) {
if (a == b || a == c || b == c)
return;
if (s.m_drating)
if (ctx.m_drating)
return;
if (!enable_eq(a, b, c))
return;
@ -149,7 +142,7 @@ namespace euf {
}
void ackerman::used_cc_eh(app* a, app* b) {
if (s.m_drating)
if (ctx.m_drating)
return;
TRACE("ack", tout << "used cc: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";);
SASSERT(a->get_decl() == b->get_decl());
@ -162,7 +155,7 @@ namespace euf {
void ackerman::gc() {
m_num_propagations_since_last_gc++;
if (m_num_propagations_since_last_gc <= s.m_config.m_dack_gc)
if (m_num_propagations_since_last_gc <= ctx.m_config.m_dack_gc)
return;
m_num_propagations_since_last_gc = 0;
@ -175,14 +168,14 @@ namespace euf {
}
void ackerman::propagate() {
SASSERT(s.s().at_base_lvl());
SASSERT(ctx.s().at_base_lvl());
auto* n = m_queue;
inference* k = nullptr;
unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.m_config.m_dack_factor);
unsigned num_prop = static_cast<unsigned>(ctx.s().get_stats().m_conflict * ctx.m_config.m_dack_factor);
num_prop = std::min(num_prop, m_table.size());
for (unsigned i = 0; i < num_prop; ++i, n = k) {
k = n->next();
if (n->m_count < s.m_config.m_dack_threshold)
if (n->m_count < ctx.m_config.m_dack_threshold)
continue;
if (n->m_count >= m_high_watermark && num_prop < m_table.size())
++num_prop;
@ -190,13 +183,12 @@ namespace euf {
add_cc(n->a, n->b);
else
add_eq(n->a, n->b, n->c);
++s.m_stats.m_ackerman;
++ctx.m_stats.m_ackerman;
remove(n);
}
}
void ackerman::add_cc(expr* _a, expr* _b) {
flet<bool> _is_redundant(s.m_is_redundant, true);
app* a = to_app(_a);
app* b = to_app(_b);
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << "\n";);
@ -204,24 +196,32 @@ namespace euf {
unsigned sz = a->get_num_args();
for (unsigned i = 0; i < sz; ++i) {
expr_ref eq = s.mk_eq(a->get_arg(i), b->get_arg(i));
lits.push_back(~s.mk_literal(eq));
expr* ai = a->get_arg(i);
expr* bi = b->get_arg(i);
if (ai != bi) {
expr_ref eq = ctx.mk_eq(ai, bi);
lits.push_back(~ctx.mk_literal(eq));
}
}
expr_ref eq = s.mk_eq(a, b);
lits.push_back(s.mk_literal(eq));
s.s().mk_clause(lits, sat::status::th(true, m.get_basic_family_id()));
expr_ref eq = ctx.mk_eq(a, b);
lits.push_back(ctx.mk_literal(eq));
th_proof_hint* ph = ctx.mk_cc_proof_hint(lits, a, b);
ctx.s().mk_clause(lits, sat::status::th(true, m.get_basic_family_id(), ph));
}
void ackerman::add_eq(expr* a, expr* b, expr* c) {
flet<bool> _is_redundant(s.m_is_redundant, true);
if (a == c || b == c)
return;
sat::literal lits[3];
expr_ref eq1(s.mk_eq(a, c), m);
expr_ref eq2(s.mk_eq(b, c), m);
expr_ref eq3(s.mk_eq(a, b), m);
expr_ref eq1(ctx.mk_eq(a, c), m);
expr_ref eq2(ctx.mk_eq(b, c), m);
expr_ref eq3(ctx.mk_eq(a, b), m);
TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";);
lits[0] = ~s.mk_literal(eq1);
lits[1] = ~s.mk_literal(eq2);
lits[2] = s.mk_literal(eq3);
s.s().mk_clause(3, lits, sat::status::th(true, m.get_basic_family_id()));
lits[0] = ~ctx.mk_literal(eq1);
lits[1] = ~ctx.mk_literal(eq2);
lits[2] = ctx.mk_literal(eq3);
th_proof_hint* ph = ctx.mk_tc_proof_hint(lits);
ctx.s().add_clause(3, lits, sat::status::th(true, m.get_basic_family_id(), ph));
}
}

View file

@ -29,12 +29,12 @@ namespace euf {
class ackerman {
struct inference : dll_base<inference>{
bool is_cc;
expr* a, *b, *c;
unsigned m_count{ 0 };
inference():is_cc(false), a(nullptr), b(nullptr), c(nullptr) {}
inference(app* a, app* b):is_cc(true), a(a), b(b), c(nullptr) {}
inference(expr* a, expr* b, expr* c):is_cc(false), a(a), b(b), c(c) {}
bool is_cc;
inference(): a(nullptr), b(nullptr), c(nullptr), is_cc(false) {}
inference(app* a, app* b): a(a), b(b), c(nullptr), is_cc(true) {}
inference(expr* a, expr* b, expr* c): a(a), b(b), c(c), is_cc(false) {}
};
struct inference_eq {
@ -52,14 +52,14 @@ namespace euf {
typedef hashtable<inference*, inference_hash, inference_eq> table_t;
solver& s;
solver& ctx;
ast_manager& m;
table_t m_table;
inference* m_queue { nullptr };
inference* m_tmp_inference { nullptr };
unsigned m_gc_threshold { 100 };
unsigned m_high_watermark { 1000 };
unsigned m_num_propagations_since_last_gc { 0 };
inference* m_queue = nullptr;
inference* m_tmp_inference = nullptr;
unsigned m_gc_threshold = 100;
unsigned m_high_watermark = 1000 ;
unsigned m_num_propagations_since_last_gc = 0;
void reset();
void new_tmp();
@ -75,7 +75,7 @@ namespace euf {
public:
ackerman(solver& s, ast_manager& m);
ackerman(solver& ctx, ast_manager& m);
~ackerman();
void cg_conflict_eh(expr * n1, expr * n2);

View file

@ -34,28 +34,28 @@ Notes:
namespace euf {
void solver::internalize(expr* e, bool redundant) {
void solver::internalize(expr* e) {
if (get_enode(e))
return;
if (si.is_bool_op(e))
attach_lit(si.internalize(e, redundant), e);
attach_lit(si.internalize(e), e);
else if (auto* ext = expr2solver(e))
ext->internalize(e, redundant);
ext->internalize(e);
else
visit_rec(m, e, false, false, redundant);
visit_rec(m, e, false, false);
SASSERT(m_egraph.find(e));
}
sat::literal solver::mk_literal(expr* e) {
expr_ref _e(e, m);
bool is_not = m.is_not(e, e);
sat::literal lit = internalize(e, false, false, m_is_redundant);
sat::literal lit = internalize(e, false, false);
if (is_not)
lit.neg();
return lit;
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
euf::enode* n = get_enode(e);
if (n) {
if (m.is_bool(e)) {
@ -67,17 +67,15 @@ namespace euf {
return sat::null_literal;
}
if (si.is_bool_op(e)) {
sat::literal lit = attach_lit(si.internalize(e, redundant), e);
sat::literal lit = attach_lit(si.internalize(e), e);
if (sign)
lit.neg();
return lit;
}
if (auto* ext = expr2solver(e))
return ext->internalize(e, sign, root, redundant);
if (!visit_rec(m, e, sign, root, redundant)) {
TRACE("euf", tout << "visit-rec\n";);
return ext->internalize(e, sign, root);
if (!visit_rec(m, e, sign, root))
return sat::null_literal;
}
SASSERT(get_enode(e));
if (m.is_bool(e))
return literal(si.to_bool_var(e), sign);
@ -89,13 +87,13 @@ namespace euf {
th_solver* s = nullptr;
if (n && !si.is_bool_op(e) && (s = expr2solver(e), s && euf::null_theory_var == n->get_th_var(s->get_id()))) {
// ensure that theory variables are attached in shared contexts. See notes (*)
s->internalize(e, false);
s->internalize(e);
return true;
}
if (n)
return true;
if (si.is_bool_op(e)) {
attach_lit(si.internalize(e, m_is_redundant), e);
attach_lit(si.internalize(e), e);
return true;
}
if (is_app(e) && to_app(e)->get_num_args() > 0) {
@ -103,7 +101,7 @@ namespace euf {
return false;
}
if (auto* s = expr2solver(e))
s->internalize(e, m_is_redundant);
s->internalize(e);
else
attach_node(mk_enode(e, 0, nullptr));
return true;
@ -118,8 +116,8 @@ namespace euf {
return false;
SASSERT(!get_enode(e));
if (auto* s = expr2solver(e))
s->internalize(e, m_is_redundant);
else
s->internalize(e);
else
attach_node(mk_enode(e, num, m_args.data()));
return true;
}
@ -159,15 +157,22 @@ namespace euf {
v = si.add_bool_var(e);
s().set_external(v);
s().set_eliminated(v, false);
set_bool_var2expr(v, e);
m_var_trail.push_back(v);
sat::literal lit2 = literal(v, false);
s().mk_clause(~lit, lit2, sat::status::th(m_is_redundant, m.get_basic_family_id()));
s().mk_clause(lit, ~lit2, sat::status::th(m_is_redundant, m.get_basic_family_id()));
th_proof_hint* ph1 = nullptr, * ph2 = nullptr;
if (use_drat()) {
ph1 = mk_smt_hint(symbol("tseitin"), ~lit, lit2);
ph2 = mk_smt_hint(symbol("tseitin"), lit, ~lit2);
}
s().mk_clause(~lit, lit2, sat::status::th(false, m.get_basic_family_id(), ph1));
s().mk_clause(lit, ~lit2, sat::status::th(false, m.get_basic_family_id(), ph2));
add_aux(~lit, lit2);
add_aux(lit, ~lit2);
lit = lit2;
}
TRACE("euf", tout << "attach v" << v << " " << mk_bounded_pp(e, m) << "\n";);
TRACE("euf", tout << "attach b" << v << " " << mk_bounded_pp(e, m) << "\n";);
m_bool_var2expr.reserve(v + 1, nullptr);
if (m_bool_var2expr[v] && m_egraph.find(e)) {
if (m_egraph.find(e)->bool_var() != v) {
@ -181,15 +186,16 @@ namespace euf {
return lit;
}
m_bool_var2expr[v] = e;
m_var_trail.push_back(v);
set_bool_var2expr(v, e);
enode* n = m_egraph.find(e);
if (!n)
n = mk_enode(e, 0, nullptr);
CTRACE("euf", n->bool_var() != sat::null_bool_var && n->bool_var() != v, display(tout << bpp(n) << " " << n->bool_var() << " vs " << v << "\n"));
SASSERT(n->bool_var() == sat::null_bool_var || n->bool_var() == v);
m_egraph.set_bool_var(n, v);
if (m.is_eq(e) || m.is_or(e) || m.is_and(e) || m.is_not(e))
m_egraph.set_merge_enabled(n, false);
if (si.is_bool_op(e))
m_egraph.set_cgc_enabled(n, false);
lbool val = s().value(lit);
if (val != l_undef)
m_egraph.set_value(n, val, justification::external(to_ptr(val == l_true ? lit : ~lit)));
@ -211,13 +217,20 @@ namespace euf {
void solver::add_not_distinct_axiom(app* e, enode* const* args) {
SASSERT(m.is_distinct(e));
unsigned sz = e->get_num_args();
sat::status st = sat::status::th(m_is_redundant, m.get_basic_family_id());
if (sz <= 1) {
s().mk_clause(0, nullptr, st);
s().mk_clause(0, nullptr, mk_distinct_status(0, nullptr));
return;
}
// check if it is trivial
expr_mark visited;
for (expr* arg : *e) {
if (visited.is_marked(arg))
return;
visited.mark(arg);
}
static const unsigned distinct_max_args = 32;
if (sz <= distinct_max_args) {
sat::literal_vector lits;
@ -229,7 +242,7 @@ namespace euf {
}
}
add_root(lits);
s().mk_clause(lits, st);
s().mk_clause(lits, mk_distinct_status(lits));
}
else {
// g(f(x_i)) = x_i
@ -242,18 +255,19 @@ namespace euf {
func_decl_ref g(m.mk_fresh_func_decl("dist-g", "", 1, &u_ptr, srt), m);
expr_ref a(m.mk_fresh_const("a", u), m);
expr_ref_vector eqs(m);
for (expr* arg : *e) {
for (expr* arg : *e) {
expr_ref fapp(m.mk_app(f, arg), m);
expr_ref gapp(m.mk_app(g, fapp.get()), m);
expr_ref eq = mk_eq(gapp, arg);
sat::literal lit = mk_literal(eq);
s().add_clause(lit, st);
s().add_clause(lit, mk_distinct_status(lit));
eqs.push_back(mk_eq(fapp, a));
}
pb_util pb(m);
expr_ref at_least2(pb.mk_at_least_k(eqs.size(), eqs.data(), 2), m);
sat::literal lit = si.internalize(at_least2, m_is_redundant);
s().add_clause(lit, st);
sat::literal lit = si.internalize(at_least2);
s().add_clause(lit, mk_distinct_status(lit));
}
}
@ -261,19 +275,19 @@ namespace euf {
SASSERT(m.is_distinct(e));
static const unsigned distinct_max_args = 32;
unsigned sz = e->get_num_args();
sat::status st = sat::status::th(m_is_redundant, m.get_basic_family_id());
if (sz <= 1)
return;
return;
sort* srt = e->get_arg(0)->get_sort();
auto sort_sz = srt->get_num_elements();
if (sort_sz.is_finite() && sort_sz.size() < sz)
s().add_clause(0, nullptr, st);
s().add_clause(0, nullptr, mk_tseitin_status(0, nullptr));
else if (sz <= distinct_max_args) {
for (unsigned i = 0; i < sz; ++i) {
for (unsigned j = i + 1; j < sz; ++j) {
expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr());
sat::literal lit = ~mk_literal(eq);
s().add_clause(lit, st);
s().add_clause(lit, mk_distinct_status(lit));
}
}
}
@ -284,25 +298,24 @@ namespace euf {
func_decl_ref f(m.mk_fresh_func_decl("dist-f", "", 1, &srt, u), m);
for (unsigned i = 0; i < sz; ++i) {
expr_ref fapp(m.mk_app(f, e->get_arg(i)), m);
expr_ref fresh(m.mk_fresh_const("dist-value", u), m);
expr_ref fresh(m.mk_model_value(i, u), m);
enode* n = mk_enode(fresh, 0, nullptr);
n->mark_interpreted();
expr_ref eq = mk_eq(fapp, fresh);
sat::literal lit = mk_literal(eq);
s().add_clause(lit, st);
s().add_clause(lit, mk_distinct_status(lit));
}
}
}
void solver::axiomatize_basic(enode* n) {
expr* e = n->get_expr();
sat::status st = sat::status::th(m_is_redundant, m.get_basic_family_id());
expr* c = nullptr, * th = nullptr, * el = nullptr;
if (!m.is_bool(e) && m.is_ite(e, c, th, el)) {
expr_ref eq_th = mk_eq(e, th);
sat::literal lit_th = mk_literal(eq_th);
if (th == el) {
s().add_clause(lit_th, st);
s().add_clause(lit_th, mk_tseitin_status(lit_th));
}
else {
sat::literal lit_c = mk_literal(c);
@ -310,8 +323,8 @@ namespace euf {
sat::literal lit_el = mk_literal(eq_el);
add_root(~lit_c, lit_th);
add_root(lit_c, lit_el);
s().add_clause(~lit_c, lit_th, st);
s().add_clause(lit_c, lit_el, st);
s().add_clause(~lit_c, lit_th, mk_tseitin_status(~lit_c, lit_th));
s().add_clause(lit_c, lit_el, mk_tseitin_status(lit_c, lit_el));
}
}
else if (m.is_distinct(e)) {
@ -324,26 +337,17 @@ namespace euf {
eqs.push_back(eq);
}
}
expr_ref fml(m.mk_or(eqs), m);
expr_ref fml = mk_or(eqs);
sat::literal dist(si.to_bool_var(e), false);
sat::literal some_eq = si.internalize(fml, m_is_redundant);
sat::literal some_eq = si.internalize(fml);
add_root(~dist, ~some_eq);
add_root(dist, some_eq);
s().add_clause(~dist, ~some_eq, st);
s().add_clause(dist, some_eq, st);
s().add_clause(~dist, ~some_eq, mk_distinct_status(~dist, ~some_eq));
s().add_clause(dist, some_eq, mk_distinct_status(dist, some_eq));
}
else if (m.is_eq(e, th, el) && !m.is_iff(e)) {
sat::literal lit1 = expr2literal(e);
s().set_phase(lit1);
expr_ref e2(m.mk_eq(el, th), m);
enode* n2 = m_egraph.find(e2);
if (n2) {
sat::literal lit2 = expr2literal(e2);
add_root(~lit1, lit2);
add_root(lit1, ~lit2);
s().add_clause(~lit1, lit2, st);
s().add_clause(lit1, ~lit2, st);
}
}
}
@ -456,32 +460,53 @@ namespace euf {
euf::enode* solver::e_internalize(expr* e) {
euf::enode* n = m_egraph.find(e);
if (!n) {
internalize(e, m_is_redundant);
internalize(e);
n = m_egraph.find(e);
}
return n;
}
euf::enode* solver::mk_enode(expr* e, unsigned n, enode* const* args) {
euf::enode* r = m_egraph.mk(e, m_generation, n, args);
for (unsigned i = 0; i < n; ++i)
ensure_merged_tf(args[i]);
return r;
}
euf::enode* solver::mk_enode(expr* e, unsigned num, enode* const* args) {
void solver::ensure_merged_tf(euf::enode* n) {
switch (n->value()) {
case l_undef:
break;
case l_true:
if (n->get_root() != mk_true())
m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var())));
break;
case l_false:
if (n->get_root() != mk_false())
m_egraph.merge(n, mk_false(), to_ptr(~sat::literal(n->bool_var())));
break;
//
// Don't track congruences of Boolean connectives or arguments.
// The assignments to associated literals is sufficient
//
if (si.is_bool_op(e))
num = 0;
//
// (if p th el) (non-Boolean case) produces clauses
// (=> p (= (if p th el) th))
// and (=> (not p) (= (if p th el) el))
// The clauses establish equalities between the ite term and
// the th or el sub-terms.
//
if (m.is_ite(e))
num = 0;
enode* n = m_egraph.mk(e, m_generation, num, args);
if (si.is_bool_op(e))
m_egraph.set_cgc_enabled(n, false);
//
// To track congruences of Boolean children under non-Boolean
// functions set the merge_tf flag to true.
//
for (unsigned i = 0; i < num; ++i) {
if (!m.is_bool(args[i]->get_sort()))
continue;
bool was_enabled = args[i]->merge_tf();
m_egraph.set_merge_tf_enabled(args[i], true);
if (!was_enabled && n->value() != l_undef && !m.is_value(n->get_root()->get_expr())) {
if (n->value() == l_true)
m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var())));
else
m_egraph.merge(n, mk_false(), to_ptr(~sat::literal(n->bool_var())));
}
}
return n;
}
}

View file

@ -287,25 +287,41 @@ namespace euf {
nodes.push_back(n);
for (unsigned i = 0; i < nodes.size(); ++i) {
euf::enode* r = nodes[i];
if (r->is_marked1())
if (!r || r->is_marked1())
continue;
r->mark1();
for (auto* arg : euf::enode_args(r))
nodes.push_back(arg);
if (is_app(r->get_expr()))
for (auto* arg : *r->get_app())
nodes.push_back(get_enode(arg));
expr_ref val = mdl(r->get_expr());
expr_ref sval(m);
th_rewriter rw(m);
rw(val, sval);
out << bpp(r) << " := " << sval << " " << mdl(r->get_root()->get_expr()) << "\n";
expr_ref mval = mdl(r->get_root()->get_expr());
if (mval != sval) {
if (r->bool_var() != sat::null_bool_var)
out << "b" << r->bool_var() << " ";
out << bpp(r) << " :=\neval: " << sval << "\nmval: " << mval << "\n";
continue;
}
if (!m.is_bool(val))
continue;
auto bval = s().value(r->bool_var());
bool tt = l_true == bval;
if (tt != m.is_true(sval))
out << bpp(r) << " :=\neval: " << sval << "\nmval: " << bval << "\n";
}
for (euf::enode* r : nodes)
r->unmark1();
if (r)
r->unmark1();
out << mdl << "\n";
}
void solver::validate_model(model& mdl) {
if (!m_unhandled_functions.empty())
return;
if (get_config().m_arith_ignore_int)
return;
for (auto* s : m_solvers)
if (s && s->has_unhandled())
return;
@ -332,6 +348,8 @@ namespace euf {
continue;
if (!is_relevant(n))
continue;
if (n->bool_var() == sat::null_bool_var)
continue;
bool tt = l_true == s().value(n->bool_var());
if (tt && !mdl.is_false(e))
continue;

View file

@ -22,20 +22,25 @@ Author:
namespace euf {
void solver::init_proof() {
if (!m_proof_initialized) {
get_drat().add_theory(get_id(), symbol("euf"));
get_drat().add_theory(m.get_basic_family_id(), symbol("bool"));
}
if (!m_proof_out && s().get_config().m_drat &&
(get_config().m_lemmas2console || s().get_config().m_smt_proof.is_non_empty_string())) {
TRACE("euf", tout << "init-proof\n");
m_proof_out = alloc(std::ofstream, s().get_config().m_smt_proof.str(), std::ios_base::out);
if (get_config().m_lemmas2console)
get_drat().set_clause_eh(*this);
if (s().get_config().m_smt_proof.is_non_empty_string())
get_drat().set_clause_eh(*this);
}
m_proof_initialized = true;
if (m_proof_initialized)
return;
if (m_on_clause && !s().get_config().m_drat_disable)
s().set_drat(true);
if (!s().get_config().m_drat)
return;
if (!get_config().m_lemmas2console &&
!s().get_config().m_smt_proof_check &&
!m_on_clause &&
!m_config.m_proof_log.is_non_empty_string())
return;
if (m_config.m_proof_log.is_non_empty_string())
m_proof_out = alloc(std::ofstream, m_config.m_proof_log.str(), std::ios_base::out);
get_drat().set_clause_eh(*this);
m_proof_initialized = true;
}
/**
@ -46,7 +51,7 @@ namespace euf {
* so it isn't necessarily an axiom over EUF,
* We will here leave it to the EUF checker to perform resolution steps.
*/
void solver::log_antecedents(literal l, literal_vector const& r) {
void solver::log_antecedents(literal l, literal_vector const& r, th_proof_hint* hint) {
TRACE("euf", log_antecedents(tout, l, r););
if (!use_drat())
return;
@ -55,7 +60,7 @@ namespace euf {
lits.push_back(~lit);
if (l != sat::null_literal)
lits.push_back(l);
get_drat().add(lits, sat::status::th(true, get_id()));
get_drat().add(lits, sat::status::th(true, get_id(), hint));
}
void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) {
@ -74,6 +79,193 @@ namespace euf {
}
}
eq_proof_hint* solver::mk_hint(symbol const& th, literal conseq, literal_vector const& r) {
if (!use_drat())
return nullptr;
push(value_trail(m_lit_tail));
push(value_trail(m_cc_tail));
push(restore_vector(m_proof_literals));
if (conseq != sat::null_literal)
m_proof_literals.push_back(~conseq);
m_proof_literals.append(r);
m_lit_head = m_lit_tail;
m_cc_head = m_cc_tail;
m_lit_tail = m_proof_literals.size();
m_cc_tail = m_explain_cc.size();
return new (get_region()) eq_proof_hint(th, m_lit_head, m_lit_tail, m_cc_head, m_cc_tail);
}
th_proof_hint* solver::mk_cc_proof_hint(sat::literal_vector const& ante, app* a, app* b) {
if (!use_drat())
return nullptr;
SASSERT(a->get_decl() == b->get_decl());
push(value_trail(m_lit_tail));
push(value_trail(m_cc_tail));
push(restore_vector(m_proof_literals));
push(restore_vector(m_explain_cc));
for (auto lit : ante)
m_proof_literals.push_back(~lit);
m_explain_cc.push_back({a, b, 0, false});
m_lit_head = m_lit_tail;
m_cc_head = m_cc_tail;
m_lit_tail = m_proof_literals.size();
m_cc_tail = m_explain_cc.size();
return new (get_region()) eq_proof_hint(m_euf, m_lit_head, m_lit_tail, m_cc_head, m_cc_tail);
}
th_proof_hint* solver::mk_tc_proof_hint(sat::literal const* clause) {
if (!use_drat())
return nullptr;
push(value_trail(m_lit_tail));
push(value_trail(m_cc_tail));
push(restore_vector(m_proof_literals));
for (unsigned i = 0; i < 3; ++i)
m_proof_literals.push_back(~clause[i]);
m_lit_head = m_lit_tail;
m_cc_head = m_cc_tail;
m_lit_tail = m_proof_literals.size();
m_cc_tail = m_explain_cc.size();
return new (get_region()) eq_proof_hint(m_euf, m_lit_head, m_lit_tail, m_cc_head, m_cc_tail);
}
expr* eq_proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
func_decl_ref cc(m), cc_comm(m);
sort* proof = m.mk_proof_sort();
expr_ref_vector& args = s.m_expr_args;
args.reset();
if (m_cc_head < m_cc_tail) {
sort* sorts[1] = { m.mk_bool_sort() };
cc_comm = m.mk_func_decl(symbol("comm"), 1, sorts, proof);
cc = m.mk_func_decl(symbol("cc"), 1, sorts, proof);
}
auto cc_proof = [&](bool comm, expr* eq) {
if (comm)
return m.mk_app(cc_comm, eq);
else
return m.mk_app(cc, eq);
};
auto compare_ts = [](cc_justification_record const& a,
cc_justification_record const& b) {
auto const& [_1, _2, ta, _3] = a;
auto const& [_4, _5, tb, _6] = b;
return ta < tb;
};
for (unsigned i = m_lit_head; i < m_lit_tail; ++i)
args.push_back(s.literal2expr(s.m_proof_literals[i]));
std::sort(s.m_explain_cc.data() + m_cc_head, s.m_explain_cc.data() + m_cc_tail, compare_ts);
for (unsigned i = m_cc_head; i < m_cc_tail; ++i) {
auto const& [a, b, ts, comm] = s.m_explain_cc[i];
args.push_back(cc_proof(comm, m.mk_eq(a, b)));
}
return m.mk_app(th, args.size(), args.data(), proof);
}
smt_proof_hint* solver::mk_smt_clause(symbol const& n, unsigned nl, literal const* lits) {
if (!use_drat())
return nullptr;
push(value_trail(m_lit_tail));
push(restore_vector(m_proof_literals));
for (unsigned i = 0; i < nl; ++i)
m_proof_literals.push_back(~lits[i]);
m_lit_head = m_lit_tail;
m_eq_head = m_eq_tail;
m_deq_head = m_deq_tail;
m_lit_tail = m_proof_literals.size();
m_eq_tail = m_proof_eqs.size();
m_deq_tail = m_proof_deqs.size();
return new (get_region()) smt_proof_hint(n, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail, m_deq_head, m_deq_tail);
}
smt_proof_hint* solver::mk_smt_hint(symbol const& n, unsigned nl, literal const* lits, unsigned ne, expr_pair const* eqs, unsigned nd, expr_pair const* deqs) {
if (!use_drat())
return nullptr;
push(value_trail(m_lit_tail));
push(restore_vector(m_proof_literals));
for (unsigned i = 0; i < nl; ++i)
if (sat::null_literal != lits[i]) {
if (!literal2expr(lits[i]))
IF_VERBOSE(0, verbose_stream() << lits[i] << "\n"; display(verbose_stream()));
SASSERT(literal2expr(lits[i]));
m_proof_literals.push_back(lits[i]);
}
push(value_trail(m_eq_tail));
push(restore_vector(m_proof_eqs));
m_proof_eqs.append(ne, eqs);
push(value_trail(m_deq_tail));
push(restore_vector(m_proof_deqs));
m_proof_deqs.append(nd, deqs);
m_lit_head = m_lit_tail;
m_eq_head = m_eq_tail;
m_deq_head = m_deq_tail;
m_lit_tail = m_proof_literals.size();
m_eq_tail = m_proof_eqs.size();
m_deq_tail = m_proof_deqs.size();
return new (get_region()) smt_proof_hint(n, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail, m_deq_head, m_deq_tail);
}
smt_proof_hint* solver::mk_smt_hint(symbol const& n, unsigned nl, literal const* lits, unsigned ne, enode_pair const* eqs) {
if (!use_drat())
return nullptr;
m_expr_pairs.reset();
for (unsigned i = 0; i < ne; ++i)
m_expr_pairs.push_back({ eqs[i].first->get_expr(), eqs[i].second->get_expr() });
return mk_smt_hint(n, nl, lits, ne, m_expr_pairs.data());
}
sat::status solver::mk_tseitin_status(sat::literal a, sat::literal b) {
sat::literal lits[2] = { a, b };
return mk_tseitin_status(2, lits);
}
sat::status solver::mk_tseitin_status(unsigned n, sat::literal const* lits) {
th_proof_hint* ph = use_drat() ? mk_smt_hint(symbol("tseitin"), n, lits) : nullptr;
return sat::status::th(false, m.get_basic_family_id(), ph);
}
sat::status solver::mk_distinct_status(unsigned n, sat::literal const* lits) {
th_proof_hint* ph = use_drat() ? mk_smt_hint(symbol("alldiff"), n, lits) : nullptr;
return sat::status::th(false, m.get_basic_family_id(), ph);
}
expr* smt_proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
sort* proof = m.mk_proof_sort();
ptr_buffer<sort> sorts;
expr_ref_vector args(m);
for (unsigned i = m_lit_head; i < m_lit_tail; ++i)
args.push_back(s.literal2expr(s.m_proof_literals[i]));
for (unsigned i = m_eq_head; i < m_eq_tail; ++i) {
auto const& [a, b] = s.m_proof_eqs[i];
args.push_back(m.mk_eq(a, b));
}
for (unsigned i = m_deq_head; i < m_deq_tail; ++i) {
auto const& [a, b] = s.m_proof_deqs[i];
args.push_back(m.mk_not(m.mk_eq(a, b)));
}
for (auto * arg : args)
sorts.push_back(arg->get_sort());
func_decl* f = m.mk_func_decl(m_name, sorts.size(), sorts.data(), proof);
return m.mk_app(f, args);
}
void solver::set_tmp_bool_var(bool_var b, expr* e) {
m_bool_var2expr.setx(b, e, nullptr);
}
@ -100,7 +292,7 @@ namespace euf {
lits.push_back(jst.lit_consequent());
if (jst.eq_consequent().first != nullptr)
lits.push_back(add_lit(jst.eq_consequent()));
get_drat().add(lits, sat::status::th(m_is_redundant, jst.ext().get_id(), jst.get_pragma()));
get_drat().add(lits, sat::status::th(false, jst.ext().get_id(), jst.get_pragma()));
for (unsigned i = s().num_vars(); i < nv; ++i)
set_tmp_bool_var(i, nullptr);
}
@ -109,6 +301,18 @@ namespace euf {
TRACE("euf", tout << "on-clause " << n << "\n");
on_lemma(n, lits, st);
on_proof(n, lits, st);
on_check(n, lits, st);
on_clause_eh(n, lits, st);
}
void solver::on_clause_eh(unsigned n, literal const* lits, sat::status st) {
if (!m_on_clause)
return;
m_clause.reset();
for (unsigned i = 0; i < n; ++i)
m_clause.push_back(literal2expr(lits[i]));
auto hint = status2proof_hint(st);
m_on_clause(m_on_clause_ctx, hint, m_clause.size(), m_clause.data());
}
void solver::on_proof(unsigned n, literal const* lits, sat::status st) {
@ -119,17 +323,32 @@ namespace euf {
if (!visit_clause(out, n, lits))
return;
if (st.is_asserted())
display_redundant(out, n, lits, status2proof_hint(st));
display_inferred(out, n, lits, status2proof_hint(st));
else if (st.is_deleted())
display_deleted(out, n, lits);
else if (st.is_redundant())
display_redundant(out, n, lits, status2proof_hint(st));
display_inferred(out, n, lits, status2proof_hint(st));
else if (st.is_input())
display_assume(out, n, lits);
else
UNREACHABLE();
out.flush();
}
void solver::on_check(unsigned n, literal const* lits, sat::status st) {
if (!s().get_config().m_smt_proof_check)
return;
m_clause.reset();
for (unsigned i = 0; i < n; ++i)
m_clause.push_back(literal2expr(lits[i]));
auto hint = status2proof_hint(st);
if (st.is_asserted() || st.is_redundant())
m_smt_proof_checker.infer(m_clause, hint);
else if (st.is_deleted())
m_smt_proof_checker.del(m_clause);
else if (st.is_input())
m_smt_proof_checker.assume(m_clause);
}
void solver::on_lemma(unsigned n, literal const* lits, sat::status st) {
if (!get_config().m_lemmas2console)
@ -162,10 +381,13 @@ namespace euf {
}
bool solver::visit_clause(std::ostream& out, unsigned n, literal const* lits) {
expr_ref k(m);
for (unsigned i = 0; i < n; ++i) {
expr* e = bool_var2expr(lits[i].var());
if (!e)
return false;
if (!e) {
k = m.mk_const(symbol(lits[i].var()), m.mk_bool_sort());
e = k;
}
visit_expr(out, e);
}
return true;
@ -179,10 +401,12 @@ namespace euf {
display_literals(out << "(assume", n, lits) << ")\n";
}
void solver::display_redundant(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint) {
if (proof_hint)
visit_expr(out, proof_hint);
display_hint(display_literals(out << "(learn", n, lits), proof_hint) << ")\n";
void solver::display_inferred(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint) {
expr_ref hint(proof_hint, m);
if (!hint)
hint = m.mk_const(m_smt, m.mk_proof_sort());
visit_expr(out, hint);
display_hint(display_literals(out << "(infer", n, lits), hint) << ")\n";
}
void solver::display_deleted(std::ostream& out, unsigned n, literal const* lits) {
@ -193,26 +417,32 @@ namespace euf {
if (proof_hint)
return display_expr(out << " ", proof_hint);
else
return out;
return out;
}
expr_ref solver::status2proof_hint(sat::status st) {
app_ref solver::status2proof_hint(sat::status st) {
if (st.is_sat())
return expr_ref(m.mk_const("rup", m.mk_proof_sort()), m); // provable by reverse unit propagation
return app_ref(m.mk_const("rup", m.mk_proof_sort()), m); // provable by reverse unit propagation
auto* h = reinterpret_cast<euf::th_proof_hint const*>(st.get_hint());
if (!h)
return expr_ref(m);
return app_ref(m);
expr* e = h->get_hint(*this);
if (e)
return expr_ref(e, m);
return app_ref(to_app(e), m);
return expr_ref(m);
return app_ref(m);
}
std::ostream& solver::display_literals(std::ostream& out, unsigned n, literal const* lits) {
expr_ref k(m);
for (unsigned i = 0; i < n; ++i) {
expr* e = bool_var2expr(lits[i].var());
if (!e) {
k = m.mk_const(symbol(lits[i].var()), m.mk_bool_sort());
e = k;
}
SASSERT(e);
if (lits[i].sign())
display_expr(out << " (not ", e) << ")";
else

View file

@ -15,36 +15,528 @@ Author:
--*/
#include "util/union_find.h"
#include "ast/ast_pp.h"
#include "ast/ast_util.h"
#include "ast/ast_ll_pp.h"
#include "ast/arith_decl_plugin.h"
#include "smt/smt_solver.h"
#include "sat/sat_params.hpp"
#include "sat/smt/euf_proof_checker.h"
#include "sat/smt/arith_proof_checker.h"
#include "sat/smt/arith_theory_checker.h"
#include "sat/smt/q_theory_checker.h"
#include "sat/smt/bv_theory_checker.h"
#include "sat/smt/distinct_theory_checker.h"
#include "sat/smt/tseitin_theory_checker.h"
namespace euf {
proof_checker::proof_checker(ast_manager& m):
/**
* The equality proof checker checks congruence proofs.
* A congruence claim comprises
* - a set of equality and diseqality literals that are
* unsatisfiable modulo equality reasoning.
* - a list of congruence claims that are used for equality reasoning.
* Congruence claims are expressions of the form
* (cc uses_commutativity (= a b))
* where uses_commutativity is true or false
* If uses commutativity is true, then a, b are (the same) binary functions
* a := f(x,y), b := f(z,u), such that x = u and y = z are consequences from
* the current equalities.
* If uses_commtativity is false, then a, b are the same n-ary expressions
* each argument position i, a_i == b_i follows from current equalities.
* If the arguments are equal according to the current equalities, then the equality
* a = b is added as a consequence.
*
* The congruence claims can be justified from the equalities in the literals.
* To be more precise, the congruence claims are justified in the they appear.
* The congruence closure algorithm (egraph) uses timestamps to record a timestamp
* when a congruence was inferred. Proof generation ensures that the congruence premises
* are sorted by the timestamp such that a congruence that depends on an earlier congruence
* appears later in the sorted order.
*
* Equality justifications are checked using union-find.
* We use union-find instead of fine-grained equality proofs (symmetry and transitivity
* of equality) assuming that it is both cheap and simple to establish a certified
* union-find checker.
*/
class eq_theory_checker : public theory_checker_plugin {
ast_manager& m;
arith_util m_arith;
expr_ref_vector m_trail;
basic_union_find m_uf;
svector<std::pair<unsigned, unsigned>> m_expr2id;
ptr_vector<expr> m_id2expr;
svector<std::pair<expr*,expr*>> m_diseqs;
unsigned m_ts = 0;
void merge(expr* x, expr* y) {
m_uf.merge(expr2id(x), expr2id(y));
IF_VERBOSE(10, verbose_stream() << "merge " << mk_bounded_pp(x, m) << " == " << mk_bounded_pp(y, m) << "\n");
merge_numeral(x);
merge_numeral(y);
}
void merge_numeral(expr* x) {
rational n;
expr* y;
if (m_arith.is_uminus(x, y) && m_arith.is_numeral(y, n)) {
y = m_arith.mk_numeral(-n, x->get_sort());
m_trail.push_back(y);
m_uf.merge(expr2id(x), expr2id(y));
}
}
bool are_equal(expr* x, expr* y) {
return m_uf.find(expr2id(x)) == m_uf.find(expr2id(y));
}
bool congruence(bool comm, app* x, app* y) {
if (x->get_decl() != y->get_decl())
return false;
if (x->get_num_args() != y->get_num_args())
return false;
if (comm) {
if (x->get_num_args() != 2)
return false;
if (!are_equal(x->get_arg(0), y->get_arg(1)))
return false;
if (!are_equal(y->get_arg(0), x->get_arg(1)))
return false;
merge(x, y);
}
else {
for (unsigned i = 0; i < x->get_num_args(); ++i)
if (!are_equal(x->get_arg(i), y->get_arg(i)))
return false;
merge(x, y);
}
IF_VERBOSE(10, verbose_stream() << "cc " << mk_bounded_pp(x, m) << " == " << mk_bounded_pp(y, m) << "\n");
return true;
}
void reset() {
++m_ts;
if (m_ts == 0) {
m_expr2id.reset();
++m_ts;
}
m_uf.reset();
m_diseqs.reset();
}
unsigned expr2id(expr* e) {
auto [ts, id] = m_expr2id.get(e->get_id(), {0,0});
if (ts != m_ts) {
id = m_uf.mk_var();
m_expr2id.setx(e->get_id(), {m_ts, id}, {0,0});
m_id2expr.setx(id, e, nullptr);
}
return id;
}
public:
eq_theory_checker(ast_manager& m): m(m), m_arith(m), m_trail(m) {}
expr_ref_vector clause(app* jst) override {
expr_ref_vector result(m);
for (expr* arg : *jst)
if (m.is_bool(arg))
result.push_back(mk_not(m, arg));
return result;
}
bool check(app* jst) override {
IF_VERBOSE(10, verbose_stream() << mk_pp(jst, m) << "\n");
reset();
for (expr* arg : *jst) {
expr* x, *y;
bool sign = m.is_not(arg, arg);
if (m.is_bool(arg)) {
if (m.is_eq(arg, x, y)) {
if (sign)
m_diseqs.push_back({x, y});
else
merge(x, y);
}
merge(arg, sign ? m.mk_false() : m.mk_true());
}
else if (m.is_proof(arg)) {
if (!is_app(arg))
return false;
app* a = to_app(arg);
if (a->get_num_args() != 1)
return false;
if (!m.is_eq(a->get_arg(0), x, y))
return false;
bool is_cc = a->get_name() == symbol("cc");
bool is_comm = a->get_name() == symbol("comm");
if (!is_cc && !is_comm)
return false;
if (!is_app(x) || !is_app(y))
return false;
if (!congruence(!is_cc, to_app(x), to_app(y))) {
IF_VERBOSE(0, verbose_stream() << "not congruent " << mk_pp(a, m) << "\n");
return false;
}
}
else {
IF_VERBOSE(0, verbose_stream() << "unrecognized argument " << mk_pp(arg, m) << "\n");
return false;
}
}
// check if a disequality is violated.
for (auto const& [a, b] : m_diseqs)
if (are_equal(a, b))
return true;
// check if some equivalence class contains two distinct values.
for (unsigned v = 0; v < m_uf.get_num_vars(); ++v) {
if (v != m_uf.find(v))
continue;
unsigned r = v;
expr* val = nullptr;
do {
expr* e = m_id2expr[v];
if (val && m.are_distinct(e, val))
return true;
if (m.is_value(e))
val = e;
v = m_uf.next(v);
}
while (r != v);
}
return false;
}
void register_plugins(theory_checker& pc) override {
pc.register_plugin(symbol("euf"), this);
pc.register_plugin(symbol("smt"), this);
}
};
/**
A resolution proof term is of the form
(res pivot proof1 proof2)
The pivot occurs with opposite signs in proof1 and proof2
*/
class res_checker : public theory_checker_plugin {
ast_manager& m;
theory_checker& pc;
public:
res_checker(ast_manager& m, theory_checker& pc): m(m), pc(pc) {}
bool check(app* jst) override {
if (jst->get_num_args() != 3)
return false;
auto [pivot, proof1, proof2] = jst->args3();
if (!m.is_bool(pivot) || !m.is_proof(proof1) || !m.is_proof(proof2))
return false;
expr* narg;
bool found1 = false, found2 = false, found3 = false, found4 = false;
for (expr* arg : pc.clause(proof1)) {
found1 |= arg == pivot;
found2 |= m.is_not(arg, narg) && narg == pivot;
}
if (found1 == found2)
return false;
for (expr* arg : pc.clause(proof2)) {
found3 |= arg == pivot;
found4 |= m.is_not(arg, narg) && narg == pivot;
}
if (found3 == found4)
return false;
if (found3 == found1)
return false;
return pc.check(proof1) && pc.check(proof2);
}
expr_ref_vector clause(app* jst) override {
expr_ref_vector result(m);
auto x = jst->args3();
auto pivot = std::get<0>(x);
auto proof1 = std::get<1>(x);
auto proof2 = std::get<2>(x);
expr* narg;
auto is_pivot = [&](expr* arg) {
if (arg == pivot)
return true;
return m.is_not(arg, narg) && narg == pivot;
};
for (expr* arg : pc.clause(proof1))
if (!is_pivot(arg))
result.push_back(arg);
for (expr* arg : pc.clause(proof2))
if (!is_pivot(arg))
result.push_back(arg);
return result;
}
void register_plugins(theory_checker& pc) override {
pc.register_plugin(symbol("res"), this);
}
};
theory_checker::theory_checker(ast_manager& m):
m(m) {
arith::proof_checker* apc = alloc(arith::proof_checker, m);
m_plugins.push_back(apc);
apc->register_plugins(*this);
(void)m;
add_plugin(alloc(arith::theory_checker, m));
add_plugin(alloc(eq_theory_checker, m));
add_plugin(alloc(res_checker, m, *this));
add_plugin(alloc(q::theory_checker, m));
add_plugin(alloc(distinct::theory_checker, m));
add_plugin(alloc(smt_theory_checker_plugin, m));
add_plugin(alloc(tseitin::theory_checker, m));
add_plugin(alloc(bv::theory_checker, m));
}
proof_checker::~proof_checker() {}
theory_checker::~theory_checker() {
}
void proof_checker::register_plugin(symbol const& rule, proof_checker_plugin* p) {
void theory_checker::add_plugin(theory_checker_plugin* p) {
m_plugins.push_back(p);
p->register_plugins(*this);
}
void theory_checker::register_plugin(symbol const& rule, theory_checker_plugin* p) {
m_map.insert(rule, p);
}
bool proof_checker::check(expr_ref_vector const& clause, expr* e, expr_ref_vector& units) {
bool theory_checker::check(expr* e) {
if (!e || !is_app(e))
return false;
units.reset();
app* a = to_app(e);
proof_checker_plugin* p = nullptr;
if (m_map.find(a->get_decl()->get_name(), p))
return p->check(clause, a, units);
return false;
theory_checker_plugin* p = nullptr;
return m_map.find(a->get_decl()->get_name(), p) && p->check(a);
}
expr_ref_vector theory_checker::clause(expr* e) {
SASSERT(is_app(e) && m_map.contains(to_app(e)->get_name()));
expr_ref_vector r = m_map[to_app(e)->get_name()]->clause(to_app(e));
return r;
}
bool theory_checker::vc(expr* e, expr_ref_vector const& clause, expr_ref_vector& v) {
SASSERT(is_app(e));
app* a = to_app(e);
theory_checker_plugin* p = nullptr;
if (m_map.find(a->get_name(), p))
return p->vc(a, clause, v);
IF_VERBOSE(10, verbose_stream() << "there is no proof plugin for " << mk_pp(e, m) << "\n");
return false;
}
bool theory_checker::check(expr_ref_vector const& clause1, expr* e, expr_ref_vector & units) {
if (!check(e))
return false;
units.reset();
expr_mark literals;
auto clause2 = clause(e);
// check that all literals in clause1 are in clause2
for (expr* arg : clause2)
literals.mark(arg, true);
for (expr* arg : clause1)
if (!literals.is_marked(arg)) {
if (m.is_not(arg, arg) && m.is_not(arg, arg) && literals.is_marked(arg)) // kludge
continue;
IF_VERBOSE(0, verbose_stream() << mk_bounded_pp(arg, m) << " not in " << clause2 << "\n");
return false;
}
// extract negated units for literals in clause2 but not in clause1
// the literals should be rup
literals.reset();
for (expr* arg : clause1)
literals.mark(arg, true);
for (expr* arg : clause2)
if (!literals.is_marked(arg))
units.push_back(mk_not(m, arg));
return true;
}
expr_ref_vector smt_theory_checker_plugin::clause(app* jst) {
expr_ref_vector result(m);
for (expr* arg : *jst)
result.push_back(mk_not(m, arg));
return result;
}
void smt_theory_checker_plugin::register_plugins(theory_checker& pc) {
pc.register_plugin(symbol("datatype"), this);
pc.register_plugin(symbol("array"), this);
pc.register_plugin(symbol("quant"), this);
pc.register_plugin(symbol("fpa"), this);
}
smt_proof_checker::smt_proof_checker(ast_manager& m, params_ref const& p):
m(m),
m_params(p),
m_checker(m),
m_sat_solver(m_params, m.limit()),
m_drat(m_sat_solver)
{
m_params.set_bool("drat.check_unsat", true);
m_params.set_bool("euf", false);
m_sat_solver.updt_params(m_params);
m_drat.updt_config();
m_rup = symbol("rup");
sat_params sp(m_params);
m_check_rup = sp.smt_proof_check_rup();
}
void smt_proof_checker::ensure_solver() {
if (!m_solver)
m_solver = mk_smt_solver(m, m_params, symbol());
}
void smt_proof_checker::log_verified(app* proof_hint, bool success) {
if (!proof_hint)
return;
symbol n = proof_hint->get_name();
if (success)
m_hint2hit.insert_if_not_there(n, 0)++;
else
m_hint2miss.insert_if_not_there(n, 0)++;
++m_num_logs;
if (m_num_logs < 100 || (m_num_logs % 1000) == 0) {
std::cout << "(proofs";
for (auto const& [k, v] : m_hint2hit)
std::cout << " +" << k << " " << v;
for (auto const& [k, v] : m_hint2miss)
std::cout << " -" << k << " " << v;
std::cout << ")\n";
}
}
bool smt_proof_checker::check_rup(expr_ref_vector const& clause) {
if (!m_check_rup)
return true;
add_units();
mk_clause(clause);
return m_drat.is_drup(m_clause.size(), m_clause.data(), m_units);
}
bool smt_proof_checker::check_rup(expr* u) {
if (!m_check_rup)
return true;
add_units();
mk_clause(u);
return m_drat.is_drup(m_clause.size(), m_clause.data(), m_units);
}
void smt_proof_checker::infer(expr_ref_vector& clause, app* proof_hint) {
if (is_rup(proof_hint) && check_rup(clause)) {
if (m_check_rup) {
log_verified(proof_hint, true);
add_clause(clause);
}
return;
}
expr_ref_vector units(m);
if (m_checker.check(clause, proof_hint, units)) {
bool units_are_rup = true;
for (expr* u : units) {
if (!m.is_true(u) && !check_rup(u)) {
std::cout << "unit " << mk_bounded_pp(u, m) << " is not rup\n";
units_are_rup = false;
}
}
if (units_are_rup) {
log_verified(proof_hint, true);
add_clause(clause);
return;
}
}
// extract a simplified verification condition in case proof validation does not work.
// quantifier instantiation can be validated as follows:
// If quantifier instantiation claims that (forall x . phi(x)) => psi using instantiation x -> t
// then check the simplified VC: phi(t) => psi.
// in case psi is the literal instantiation, then the clause is a propositional tautology.
// The VC function is a no-op if the proof hint does not have an associated vc generator.
expr_ref_vector vc(clause);
if (m_checker.vc(proof_hint, clause, vc)) {
log_verified(proof_hint, true);
add_clause(clause);
return;
}
log_verified(proof_hint, false);
ensure_solver();
m_solver->push();
for (expr* lit : vc)
m_solver->assert_expr(m.mk_not(lit));
lbool is_sat = m_solver->check_sat();
if (is_sat != l_false) {
std::cout << "did not verify: " << is_sat << " " << clause << "\n";
std::cout << "vc:\n" << vc << "\n";
if (proof_hint)
std::cout << "hint: " << mk_bounded_pp(proof_hint, m, 4) << "\n";
m_solver->display(std::cout);
if (is_sat == l_true) {
model_ref mdl;
m_solver->get_model(mdl);
mdl->evaluate_constants();
std::cout << *mdl << "\n";
}
exit(0);
}
m_solver->pop(1);
std::cout << "(verified-smt";
if (proof_hint) std::cout << "\n" << mk_bounded_pp(proof_hint, m, 4);
for (expr* arg : clause)
std::cout << "\n " << mk_bounded_pp(arg, m);
std::cout << ")\n";
if (is_rup(proof_hint))
diagnose_rup_failure(clause);
add_clause(clause);
}
void smt_proof_checker::diagnose_rup_failure(expr_ref_vector const& clause) {
expr_ref_vector fmls(m), assumptions(m), core(m);
m_solver->get_assertions(fmls);
for (unsigned i = 0; i < fmls.size(); ++i) {
assumptions.push_back(m.mk_fresh_const("a", m.mk_bool_sort()));
fmls[i] = m.mk_implies(assumptions.back(), fmls.get(i));
}
ref<::solver> core_solver = mk_smt_solver(m, m_params, symbol());
// core_solver->assert_expr(fmls);
core_solver->assert_expr(m.mk_not(mk_or(clause)));
lbool ch = core_solver->check_sat(assumptions);
std::cout << "failed to verify\n" << clause << "\n";
if (ch == l_false) {
core_solver->get_unsat_core(core);
std::cout << "core\n";
for (expr* f : core)
std::cout << mk_pp(f, m) << "\n";
}
SASSERT(false);
exit(0);
}
void smt_proof_checker::collect_statistics(statistics& st) const {
if (m_solver)
m_solver->collect_statistics(st);
}
}

View file

@ -19,28 +19,147 @@ Author:
#include "util/map.h"
#include "util/scoped_ptr_vector.h"
#include "ast/ast.h"
#include "ast/ast_util.h"
#include "solver/solver.h"
#include "sat/sat_solver.h"
#include "sat/sat_drat.h"
namespace euf {
class proof_checker;
class theory_checker;
class proof_checker_plugin {
class theory_checker_plugin {
public:
virtual ~proof_checker_plugin() {}
virtual bool check(expr_ref_vector const& clause, app* jst, expr_ref_vector& units) = 0;
virtual void register_plugins(proof_checker& pc) = 0;
virtual ~theory_checker_plugin() {}
virtual bool check(app* jst) = 0;
virtual expr_ref_vector clause(app* jst) = 0;
virtual void register_plugins(theory_checker& pc) = 0;
virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.reset(); v.append(this->clause(jst)); return false; }
};
class proof_checker {
class theory_checker {
ast_manager& m;
scoped_ptr_vector<proof_checker_plugin> m_plugins;
map<symbol, proof_checker_plugin*, symbol_hash_proc, symbol_eq_proc> m_map;
scoped_ptr_vector<theory_checker_plugin> m_plugins; // plugins of proof checkers
map<symbol, theory_checker_plugin*, symbol_hash_proc, symbol_eq_proc> m_map; // symbol table of proof checkers
void add_plugin(theory_checker_plugin* p);
public:
proof_checker(ast_manager& m);
~proof_checker();
void register_plugin(symbol const& rule, proof_checker_plugin*);
theory_checker(ast_manager& m);
~theory_checker();
void register_plugin(symbol const& rule, theory_checker_plugin*);
bool check(expr* jst);
expr_ref_vector clause(expr* jst);
bool vc(expr* jst, expr_ref_vector const& clause, expr_ref_vector& v);
bool check(expr_ref_vector const& clause, expr* e, expr_ref_vector& units);
};
/**
Base class for checking SMT proofs whose justifications are
provided as a set of literals and E-node equalities.
It provides shared implementations for clause and register_plugin.
It overrides check to always fail.
*/
class smt_theory_checker_plugin : public theory_checker_plugin {
ast_manager& m;
public:
smt_theory_checker_plugin(ast_manager& m): m(m) {}
bool check(app* jst) override { return false; }
expr_ref_vector clause(app* jst) override;
void register_plugins(theory_checker& pc) override;
};
class smt_proof_checker {
ast_manager& m;
params_ref m_params;
// for checking proof rules (hints)
euf::theory_checker m_checker;
// for fallback SMT checker
scoped_ptr<::solver> m_solver;
// for RUP
symbol m_rup;
sat::solver m_sat_solver;
sat::drat m_drat;
sat::literal_vector m_units;
sat::literal_vector m_clause;
bool m_check_rup = false;
// for logging
map<symbol, unsigned, symbol_hash_proc, symbol_eq_proc> m_hint2hit, m_hint2miss;
unsigned m_num_logs = 0;
void add_units() {
auto const& units = m_drat.units();
for (unsigned i = m_units.size(); i < units.size(); ++i)
m_units.push_back(units[i].first);
}
void log_verified(app* proof_hint, bool success);
void diagnose_rup_failure(expr_ref_vector const& clause);
void ensure_solver();
public:
smt_proof_checker(ast_manager& m, params_ref const& p);
bool is_rup(app* proof_hint) {
return
proof_hint &&
proof_hint->get_name() == m_rup;
}
void mk_clause(expr_ref_vector const& clause) {
m_clause.reset();
for (expr* e : clause) {
bool sign = false;
while (m.is_not(e, e))
sign = !sign;
m_clause.push_back(sat::literal(e->get_id(), sign));
}
}
void mk_clause(expr* e) {
m_clause.reset();
bool sign = false;
while (m.is_not(e, e))
sign = !sign;
m_clause.push_back(sat::literal(e->get_id(), sign));
}
bool check_rup(expr_ref_vector const& clause);
bool check_rup(expr* u);
void add_clause(expr_ref_vector const& clause) {
if (!m_check_rup)
return;
mk_clause(clause);
m_drat.add(m_clause, sat::status::input());
}
void assume(expr_ref_vector const& clause) {
add_clause(clause);
if (!m_check_rup)
return;
ensure_solver();
m_solver->assert_expr(mk_or(clause));
}
void del(expr_ref_vector const& clause) {
}
void infer(expr_ref_vector& clause, app* proof_hint);
void collect_statistics(statistics& st) const;
};
}

View file

@ -46,11 +46,13 @@ namespace euf {
m_trail(),
m_rewriter(m),
m_unhandled_functions(m),
m_lookahead(nullptr),
m_to_m(&m),
m_to_si(&si),
m_values(m),
m_clause_visitor(m)
m_clause_visitor(m),
m_smt_proof_checker(m, p),
m_clause(m),
m_expr_args(m),
m_values(m)
{
updt_params(p);
m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2);
@ -61,6 +63,11 @@ namespace euf {
};
m_egraph.set_display_justification(disp);
std::function<void(euf::enode* n, euf::enode* ante)> on_literal = [&](enode* n, enode* ante) {
propagate_literal(n, ante);
};
m_egraph.set_on_propagate(on_literal);
if (m_relevancy.enabled()) {
std::function<void(euf::enode* root, euf::enode* other)> on_merge =
[&](enode* root, enode* other) {
@ -72,6 +79,7 @@ namespace euf {
void solver::updt_params(params_ref const& p) {
m_config.updt_params(p);
use_drat();
}
/**
@ -146,8 +154,6 @@ namespace euf {
void solver::add_solver(th_solver* th) {
family_id fid = th->get_id();
if (use_drat())
s().get_drat().add_theory(fid, th->name());
th->set_solver(m_solver);
th->push_scopes(s().num_scopes() + s().num_user_scopes());
m_solvers.push_back(th);
@ -166,8 +172,9 @@ namespace euf {
IF_VERBOSE(0, verbose_stream() << mk_pp(f, m) << " not handled\n");
}
void solver::init_search() {
void solver::init_search() {
TRACE("before_search", s().display(tout););
m_reason_unknown.clear();
for (auto* s : m_solvers)
s->init_search();
}
@ -198,14 +205,42 @@ namespace euf {
s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx));
}
/**
Retrieve set of literals r that imply r.
Since the set of literals are retrieved modulo multiple theories in a single implication
we lose theory specific justifications. For proof logging we use a catch all rule "smt"
for the case where an equality is derived using more than congruence closure.
To create fully decomposed justifications it will be necessary to augment the justification
data-structure with information about the equality that is implied by the theory.
Then each justification will imply an equality s = t assuming literals 'r'.
The theory lemma is then r -> s = t, where s = t is an equality that is available for the EUF hint.
The EUF hint is resolved against r -> s = t to eliminate s = t and to create the resulting explanation.
Example:
x - 3 = 0 => x = 3 by arithmetic
x = 3 => f(x) = f(3) by EUF
resolve to produce clause x - 3 = 0 => f(x) = f(3)
The last argument to get_assumptions is a place-holder to retrieve a justification of a propagation.
Theory solver would have to populate this hint and the combined hint would have to be composed from the
sub-hints.
*/
void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) {
m_egraph.begin_explain();
m_explain.reset();
if (use_drat() && !probing) {
push(restore_vector(m_explain_cc));
}
auto* ext = sat::constraint_base::to_extension(idx);
th_proof_hint* hint = nullptr;
bool has_theory = false;
if (ext == this)
get_antecedents(l, constraint::from_idx(idx), r, probing);
else
else {
ext->get_antecedents(l, idx, r, probing);
has_theory = true;
}
for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) {
size_t* e = m_explain[qhead];
if (is_literal(e))
@ -216,36 +251,41 @@ namespace euf {
SASSERT(ext != this);
sat::literal lit = sat::null_literal;
ext->get_antecedents(lit, idx, r, probing);
has_theory = true;
}
}
m_egraph.end_explain();
if (use_drat() && !probing)
hint = mk_hint(has_theory ? m_smt : m_euf, l, r);
unsigned j = 0;
for (sat::literal lit : r)
if (s().lvl(lit) > 0) r[j++] = lit;
r.shrink(j);
TRACE("euf", tout << "explain " << l << " <- " << r << " " << probing << "\n";);
CTRACE("euf", probing, tout << "explain " << l << " <- " << r << "\n");
DEBUG_CODE(for (auto lit : r) SASSERT(s().value(lit) == l_true););
if (!probing)
log_antecedents(l, r);
log_antecedents(l, r, hint);
}
void solver::get_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing) {
for (auto lit : euf::th_explain::lits(jst))
r.push_back(lit);
for (auto eq : euf::th_explain::eqs(jst))
add_antecedent(eq.first, eq.second);
add_antecedent(probing, eq.first, eq.second);
if (!probing && use_drat())
log_justification(l, jst);
}
void solver::add_antecedent(enode* a, enode* b) {
m_egraph.explain_eq<size_t>(m_explain, a, b);
void solver::add_antecedent(bool probing, enode* a, enode* b) {
cc_justification* cc = (!probing && use_drat()) ? &m_explain_cc : nullptr;
m_egraph.explain_eq<size_t>(m_explain, cc, a, b);
}
void solver::add_diseq_antecedent(ptr_vector<size_t>& ex, enode* a, enode* b) {
sat::bool_var v = get_egraph().explain_diseq(ex, a, b);
void solver::add_diseq_antecedent(ptr_vector<size_t>& ex, cc_justification* cc, enode* a, enode* b) {
sat::bool_var v = get_egraph().explain_diseq(ex, cc, a, b);
SASSERT(v == sat::null_bool_var || s().value(v) == l_false);
if (v != sat::null_bool_var)
ex.push_back(to_ptr(sat::literal(v, true)));
@ -261,14 +301,17 @@ namespace euf {
void solver::get_antecedents(literal l, constraint& j, literal_vector& r, bool probing) {
expr* e = nullptr;
euf::enode* n = nullptr;
cc_justification* cc = nullptr;
if (!probing && !m_drating)
init_ackerman();
if (!probing && use_drat())
cc = &m_explain_cc;
switch (j.kind()) {
case constraint::kind_t::conflict:
SASSERT(m_egraph.inconsistent());
m_egraph.explain<size_t>(m_explain);
m_egraph.explain<size_t>(m_explain, cc);
break;
case constraint::kind_t::eq:
e = m_bool_var2expr[l.var()];
@ -276,15 +319,25 @@ namespace euf {
SASSERT(n);
SASSERT(n->is_equality());
SASSERT(!l.sign());
m_egraph.explain_eq<size_t>(m_explain, n->get_arg(0), n->get_arg(1));
m_egraph.explain_eq<size_t>(m_explain, cc, n->get_arg(0), n->get_arg(1));
break;
case constraint::kind_t::lit:
case constraint::kind_t::lit: {
e = m_bool_var2expr[l.var()];
n = m_egraph.find(e);
enode* ante = j.node();
SASSERT(n);
SASSERT(m.is_bool(n->get_expr()));
m_egraph.explain_eq<size_t>(m_explain, n, (l.sign() ? mk_false() : mk_true()));
SASSERT(ante->get_root() == n->get_root());
m_egraph.explain_eq<size_t>(m_explain, cc, n, ante);
if (!m.is_true(ante->get_expr()) && !m.is_false(ante->get_expr())) {
bool_var v = ante->bool_var();
lbool val = ante->value();
SASSERT(val != l_undef);
literal ante(v, val == l_false);
m_explain.push_back(to_ptr(ante));
}
break;
}
default:
IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n");
UNREACHABLE();
@ -297,11 +350,9 @@ namespace euf {
}
void solver::asserted(literal l) {
m_relevancy.asserted(l);
if (!m_relevancy.is_relevant(l))
return;
expr* e = m_bool_var2expr.get(l.var(), nullptr);
TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " := " << mk_bounded_pp(e, m) << "\n";);
if (!e)
@ -309,24 +360,31 @@ namespace euf {
euf::enode* n = m_egraph.find(e);
if (!n)
return;
bool sign = l.sign();
m_egraph.set_value(n, sign ? l_false : l_true, justification::external(to_ptr(l)));
bool sign = l.sign();
lbool old_value = n->value();
lbool new_value = sign ? l_false : l_true;
m_egraph.set_value(n, new_value, justification::external(to_ptr(l)));
if (old_value == l_undef && n->cgc_enabled()) {
for (enode* k : enode_class(n)) {
if (k->bool_var() == sat::null_bool_var)
continue;
if (k->value() == new_value)
continue;
literal litk(k->bool_var(), sign);
if (s().value(litk) == l_true)
continue;
auto& c = lit_constraint(n);
propagate(litk, c.to_index());
if (s().value(litk) == l_false)
return;
}
}
for (auto const& th : enode_th_vars(n))
m_id2solver[th.get_id()]->asserted(l);
size_t* c = to_ptr(l);
SASSERT(is_literal(c));
SASSERT(l == get_literal(c));
if (n->value_conflict()) {
euf::enode* nb = sign ? mk_false() : mk_true();
euf::enode* r = n->get_root();
euf::enode* rb = sign ? mk_true() : mk_false();
sat::literal rl(r->bool_var(), r->value() == l_false);
m_egraph.merge(n, nb, c);
m_egraph.merge(r, rb, to_ptr(rl));
SASSERT(m_egraph.inconsistent());
return;
}
if (n->merge_tf()) {
euf::enode* nb = sign ? mk_false() : mk_true();
m_egraph.merge(n, nb, c);
@ -338,9 +396,17 @@ namespace euf {
m_egraph.new_diseq(n);
else
m_egraph.merge(n->get_arg(0), n->get_arg(1), c);
}
}
}
constraint& solver::lit_constraint(enode* n) {
void* mem = get_region().allocate(sat::constraint_base::obj_size(sizeof(constraint)));
auto* c = new (sat::constraint_base::ptr2mem(mem)) constraint(n);
sat::constraint_base::initialize(mem, this);
return *c;
}
bool solver::unit_propagate() {
bool propagated = false;
@ -353,7 +419,6 @@ namespace euf {
}
bool propagated1 = false;
if (m_egraph.propagate()) {
propagate_literals();
propagate_th_eqs();
propagated1 = true;
}
@ -374,45 +439,52 @@ namespace euf {
return propagated;
}
void solver::propagate_literals() {
for (; m_egraph.has_literal() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_literal()) {
auto [n, is_eq] = m_egraph.get_literal();
expr* e = n->get_expr();
expr* a = nullptr, *b = nullptr;
bool_var v = n->bool_var();
SASSERT(m.is_bool(e));
size_t cnstr;
literal lit;
if (is_eq) {
VERIFY(m.is_eq(e, a, b));
cnstr = eq_constraint().to_index();
lit = literal(v, false);
}
else {
lbool val = n->get_root()->value();
if (val == l_undef && m.is_false(n->get_root()->get_expr()))
val = l_false;
if (val == l_undef && m.is_true(n->get_root()->get_expr()))
val = l_true;
a = e;
b = (val == l_true) ? m.mk_true() : m.mk_false();
SASSERT(val != l_undef);
cnstr = lit_constraint().to_index();
lit = literal(v, val == l_false);
}
unsigned lvl = s().scope_lvl();
CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << is_eq << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";);
if (s().value(lit) == l_false && m_ackerman)
m_ackerman->cg_conflict_eh(a, b);
switch (s().value(lit)) {
case l_true:
break;
case l_undef:
case l_false:
s().assign(lit, sat::justification::mk_ext_justification(lvl, cnstr));
break;
void solver::propagate_literal(enode* n, enode* ante) {
expr* e = n->get_expr();
expr* a = nullptr, *b = nullptr;
bool_var v = n->bool_var();
if (v == sat::null_bool_var)
return;
SASSERT(m.is_bool(e));
size_t cnstr;
literal lit;
if (!ante) {
VERIFY(m.is_eq(e, a, b));
cnstr = eq_constraint().to_index();
lit = literal(v, false);
}
else {
//
// There are the following three cases for propagation of literals
//
// 1. n == ante is true from equallity, ante = true/false
// 2. n == ante is true from equality, value(ante) != l_undef
// 3. value(n) != l_undef, ante = true/false, merge_tf is set on n
//
lbool val = ante->value();
if (val == l_undef) {
SASSERT(m.is_value(ante->get_expr()));
val = m.is_true(ante->get_expr()) ? l_true : l_false;
}
auto& c = lit_constraint(ante);
cnstr = c.to_index();
lit = literal(v, val == l_false);
}
unsigned lvl = s().scope_lvl();
CTRACE("euf", s().value(lit) != l_true, tout << lit << " " << s().value(lit) << "@" << lvl << " " << mk_bounded_pp(a, m) << " = " << mk_bounded_pp(b, m) << "\n";);
if (s().value(lit) == l_false && m_ackerman && a && b)
m_ackerman->cg_conflict_eh(a, b);
switch (s().value(lit)) {
case l_true:
if (n->merge_tf() && !m.is_value(n->get_root()->get_expr()))
m_egraph.merge(n, ante, to_ptr(lit));
break;
case l_undef:
case l_false:
s().assign(lit, sat::justification::mk_ext_justification(lvl, cnstr));
break;
}
}
@ -422,7 +494,7 @@ namespace euf {
m_egraph.begin_explain();
m_explain.reset();
m_egraph.explain_eq<size_t>(m_explain, e.child(), e.root());
m_egraph.explain_eq<size_t>(m_explain, nullptr, e.child(), e.root());
m_egraph.end_explain();
if (m_egraph.uses_congruence())
return false;
@ -472,6 +544,7 @@ namespace euf {
sat::check_result solver::check() {
++m_stats.m_final_checks;
TRACE("euf", s().display(tout););
TRACE("final_check", s().display(tout););
bool give_up = false;
bool cont = false;
@ -482,7 +555,7 @@ namespace euf {
auto apply_solver = [&](th_solver* e) {
switch (e->check()) {
case sat::check_result::CR_CONTINUE: cont = true; break;
case sat::check_result::CR_GIVEUP: give_up = true; break;
case sat::check_result::CR_GIVEUP: m_reason_unknown = "incomplete theory " + e->name().str(); TRACE("euf", tout << "give up " << e->name() << "\n"); give_up = true; break;
default: break;
}
};
@ -490,8 +563,10 @@ namespace euf {
cont = true;
for (unsigned i = 0; i < m_solvers.size(); ++i) {
auto* e = m_solvers[i];
if (!m.inc())
if (!m.inc()) {
m_reason_unknown = "canceled";
return sat::check_result::CR_GIVEUP;
}
if (e == m_qsolver)
continue;
apply_solver(e);
@ -504,7 +579,7 @@ namespace euf {
return sat::check_result::CR_CONTINUE;
if (cont)
return sat::check_result::CR_CONTINUE;
if (m_qsolver)
if (m_qsolver && !m_config.m_arith_ignore_int)
apply_solver(m_qsolver);
if (num_nodes < m_egraph.num_nodes())
return sat::check_result::CR_CONTINUE;
@ -512,7 +587,9 @@ namespace euf {
return sat::check_result::CR_CONTINUE;
TRACE("after_search", s().display(tout););
if (give_up)
return sat::check_result::CR_GIVEUP;
return sat::check_result::CR_GIVEUP;
if (m_qsolver && m_config.m_arith_ignore_int)
return sat::check_result::CR_GIVEUP;
return sat::check_result::CR_DONE;
}
@ -522,15 +599,18 @@ namespace euf {
euf::enode* n = m_egraph.nodes()[i];
if (!m.is_bool(n->get_expr()) || !is_shared(n))
continue;
if (n->value() == l_true && !m.is_true(n->get_root()->get_expr())) {
if (n->value() == l_true && n->cgc_enabled() && !m.is_true(n->get_root()->get_expr())) {
TRACE("euf", tout << "merge " << bpp(n) << "\n");
m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var())));
merged = true;
}
if (n->value() == l_false && !m.is_false(n->get_root()->get_expr())) {
if (n->value() == l_false && n->cgc_enabled() && !m.is_false(n->get_root()->get_expr())) {
TRACE("euf", tout << "merge " << bpp(n) << "\n");
m_egraph.merge(n, mk_false(), to_ptr(~sat::literal(n->bool_var())));
merged = true;
}
}
CTRACE("euf", merged, tout << "shared bools merged\n");
return merged;
}
@ -607,28 +687,32 @@ namespace euf {
for (auto const& [e, generation, v] : m_reinit)
replay.m.insert(e, v);
TRACE("euf", for (auto const& kv : replay.m) tout << kv.m_value << "\n";);
TRACE("euf", for (auto const& kv : replay.m) tout << "b" << kv.m_value << "\n";);
for (auto const& [e, generation, v] : m_reinit) {
scoped_generation _sg(*this, generation);
TRACE("euf", tout << "replay: " << v << " " << e->get_id() << " " << mk_bounded_pp(e, m) << " " << si.is_bool_op(e) << "\n";);
TRACE("euf", tout << "replay: b" << v << " #" << e->get_id() << " " << mk_bounded_pp(e, m) << " " << si.is_bool_op(e) << "\n";);
sat::literal lit;
if (si.is_bool_op(e))
lit = literal(replay.m[e], false);
else
lit = si.internalize(e, false);
lit = si.internalize(e);
VERIFY(lit.var() == v);
if (!m_egraph.find(e) && !m.is_iff(e) && !m.is_or(e) && !m.is_and(e) && !m.is_not(e) && !m.is_implies(e) && !m.is_xor(e)) {
ptr_buffer<euf::enode> args;
if (is_app(e))
for (expr* arg : *to_app(e))
args.push_back(e_internalize(arg));
internalize(e, true);
internalize(e);
if (!m_egraph.find(e))
mk_enode(e, args.size(), args.data());
}
else
attach_lit(lit, e);
}
for (auto const& [e, v] : replay.m)
if (si.is_bool_op(e) && !si.is_cached(to_app(e), sat::literal(v, false)))
si.cache(to_app(e), sat::literal(v, false));
if (relevancy_enabled())
for (auto const& [e, generation, v] : m_reinit)
@ -648,10 +732,10 @@ namespace euf {
disable_relevancy(e);
return;
}
auto lit = si.internalize(e, true);
auto lit = si.internalize(e);
switch (to_app(e)->get_decl_kind()) {
case OP_NOT: {
auto lit2 = si.internalize(to_app(e)->get_arg(0), true);
auto lit2 = si.internalize(to_app(e)->get_arg(0));
add_aux(lit, lit2);
add_aux(~lit, ~lit2);
break;
@ -661,8 +745,8 @@ namespace euf {
disable_relevancy(e);
return;
}
auto lit1 = si.internalize(to_app(e)->get_arg(0), true);
auto lit2 = si.internalize(to_app(e)->get_arg(1), true);
auto lit1 = si.internalize(to_app(e)->get_arg(0));
auto lit2 = si.internalize(to_app(e)->get_arg(1));
add_aux(~lit, ~lit1, lit2);
add_aux(~lit, lit1, ~lit2);
add_aux(lit, lit1, lit2);
@ -672,7 +756,7 @@ namespace euf {
case OP_OR: {
sat::literal_vector lits;
for (expr* arg : *to_app(e))
lits.push_back(si.internalize(arg, true));
lits.push_back(si.internalize(arg));
for (auto lit2 : lits)
add_aux(~lit2, lit);
lits.push_back(~lit);
@ -682,7 +766,7 @@ namespace euf {
case OP_AND: {
sat::literal_vector lits;
for (expr* arg : *to_app(e))
lits.push_back(~si.internalize(arg, true));
lits.push_back(~si.internalize(arg));
for (auto nlit2 : lits)
add_aux(~lit, ~nlit2);
lits.push_back(lit);
@ -696,9 +780,9 @@ namespace euf {
add_aux(~lit);
break;
case OP_ITE: {
auto lit1 = si.internalize(to_app(e)->get_arg(0), true);
auto lit2 = si.internalize(to_app(e)->get_arg(1), true);
auto lit3 = si.internalize(to_app(e)->get_arg(2), true);
auto lit1 = si.internalize(to_app(e)->get_arg(0));
auto lit2 = si.internalize(to_app(e)->get_arg(1));
auto lit3 = si.internalize(to_app(e)->get_arg(2));
add_aux(~lit, ~lit1, lit2);
add_aux(~lit, lit1, lit3);
add_aux(lit, ~lit1, ~lit2);
@ -710,8 +794,8 @@ namespace euf {
disable_relevancy(e);
break;
}
auto lit1 = si.internalize(to_app(e)->get_arg(0), true);
auto lit2 = si.internalize(to_app(e)->get_arg(1), true);
auto lit1 = si.internalize(to_app(e)->get_arg(0));
auto lit2 = si.internalize(to_app(e)->get_arg(1));
add_aux(lit, ~lit1, lit2);
add_aux(lit, lit1, ~lit2);
add_aux(~lit, lit1, lit2);
@ -723,8 +807,8 @@ namespace euf {
disable_relevancy(e);
break;
}
auto lit1 = si.internalize(to_app(e)->get_arg(0), true);
auto lit2 = si.internalize(to_app(e)->get_arg(1), true);
auto lit1 = si.internalize(to_app(e)->get_arg(0));
auto lit2 = si.internalize(to_app(e)->get_arg(1));
add_aux(~lit, ~lit1, lit2);
add_aux(lit, lit1);
add_aux(lit, ~lit2);
@ -847,7 +931,7 @@ namespace euf {
if (m.is_eq(e) && !m.is_iff(e))
ok = false;
euf::enode* n = get_enode(e);
if (n && n->merge_enabled())
if (n && n->cgc_enabled())
ok = false;
(void)ok;
@ -896,7 +980,7 @@ namespace euf {
case constraint::kind_t::eq:
return out << "euf equality propagation";
case constraint::kind_t::lit:
return out << "euf literal propagation";
return out << "euf literal propagation " << m_egraph.bpp(c.node()) ;
default:
UNREACHABLE();
return out;
@ -918,6 +1002,7 @@ namespace euf {
m_egraph.collect_statistics(st);
for (auto* e : m_solvers)
e->collect_statistics(st);
m_smt_proof_checker.collect_statistics(st);
st.update("euf ackerman", m_stats.m_ackerman);
st.update("euf final check", m_stats.m_final_checks);
}
@ -1069,6 +1154,14 @@ namespace euf {
return true;
}
void solver::register_on_clause(
void* ctx,
user_propagator::on_clause_eh_t& on_clause) {
m_on_clause_ctx = ctx;
m_on_clause = on_clause;
init_proof();
}
void solver::user_propagate_init(
void* ctx,
user_propagator::push_eh_t& push_eh,

View file

@ -19,15 +19,17 @@ Author:
#include "util/scoped_ptr_vector.h"
#include "util/trail.h"
#include "ast/ast_translation.h"
#include "ast/ast_util.h"
#include "ast/euf/euf_egraph.h"
#include "ast/rewriter/th_rewriter.h"
#include "tactic/model_converter.h"
#include "ast/converters/model_converter.h"
#include "sat/sat_extension.h"
#include "sat/smt/atom2bool_var.h"
#include "sat/smt/sat_th.h"
#include "sat/smt/euf_ackerman.h"
#include "sat/smt/user_solver.h"
#include "sat/smt/euf_relevancy.h"
#include "sat/smt/euf_proof_checker.h"
#include "smt/params/smt_params.h"
@ -43,9 +45,12 @@ namespace euf {
enum class kind_t { conflict, eq, lit };
private:
kind_t m_kind;
enode* m_node = nullptr;
public:
constraint(kind_t k) : m_kind(k) {}
constraint(enode* n): m_kind(kind_t::lit), m_node(n) {}
kind_t kind() const { return m_kind; }
enode* node() const { SASSERT(kind() == kind_t::lit); return m_node; }
static constraint& from_idx(size_t z) {
return *reinterpret_cast<constraint*>(sat::constraint_base::idx2mem(z));
}
@ -60,9 +65,29 @@ namespace euf {
std::ostream& display(std::ostream& out) const;
};
class eq_proof_hint : public th_proof_hint {
symbol th;
unsigned m_lit_head, m_lit_tail, m_cc_head, m_cc_tail;
public:
eq_proof_hint(symbol const& th, unsigned lh, unsigned lt, unsigned ch, unsigned ct):
th(th), m_lit_head(lh), m_lit_tail(lt), m_cc_head(ch), m_cc_tail(ct) {}
expr* get_hint(euf::solver& s) const override;
};
class smt_proof_hint : public th_proof_hint {
symbol m_name;
unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail, m_deq_head, m_deq_tail;
public:
smt_proof_hint(symbol const& n, unsigned lh, unsigned lt, unsigned ch, unsigned ct, unsigned dh, unsigned dt):
m_name(n), m_lit_head(lh), m_lit_tail(lt), m_eq_head(ch), m_eq_tail(ct), m_deq_head(dh), m_deq_tail(dt) {}
expr* get_hint(euf::solver& s) const override;
};
class solver : public sat::extension, public th_internalizer, public th_decompile, public sat::clause_eh {
typedef top_sort<euf::enode> deps_t;
friend class ackerman;
friend class eq_proof_hint;
friend class smt_proof_hint;
class user_sort;
struct stats {
unsigned m_ackerman;
@ -89,35 +114,55 @@ namespace euf {
}
std::function<::solver*(void)> m_mk_solver;
user_propagator::on_clause_eh_t m_on_clause;
ast_manager& m;
sat::sat_internalizer& si;
relevancy m_relevancy;
smt_params m_config;
euf::egraph m_egraph;
trail_stack m_trail;
stats m_stats;
th_rewriter m_rewriter;
func_decl_ref_vector m_unhandled_functions;
sat::lookahead* m_lookahead = nullptr;
ast_manager* m_to_m;
sat::sat_internalizer* m_to_si;
scoped_ptr<euf::ackerman> m_ackerman;
user_solver::solver* m_user_propagator = nullptr;
th_solver* m_qsolver = nullptr;
unsigned m_generation = 0;
mutable ptr_vector<expr> m_todo;
sat::sat_internalizer& si;
relevancy m_relevancy;
smt_params m_config;
euf::egraph m_egraph;
trail_stack m_trail;
stats m_stats;
th_rewriter m_rewriter;
func_decl_ref_vector m_unhandled_functions;
sat::lookahead* m_lookahead = nullptr;
ast_manager* m_to_m = nullptr;
sat::sat_internalizer* m_to_si;
scoped_ptr<euf::ackerman> m_ackerman;
void* m_on_clause_ctx = nullptr;
user_solver::solver* m_user_propagator = nullptr;
th_solver* m_qsolver = nullptr;
unsigned m_generation = 0;
std::string m_reason_unknown;
mutable ptr_vector<expr> m_todo;
ptr_vector<expr> m_bool_var2expr;
ptr_vector<size_t> m_explain;
unsigned m_num_scopes = 0;
unsigned_vector m_var_trail;
svector<scope> m_scopes;
scoped_ptr_vector<th_solver> m_solvers;
ptr_vector<th_solver> m_id2solver;
ptr_vector<expr> m_bool_var2expr;
ptr_vector<size_t> m_explain;
euf::cc_justification m_explain_cc;
unsigned m_num_scopes = 0;
unsigned_vector m_var_trail;
svector<scope> m_scopes;
scoped_ptr_vector<th_solver> m_solvers;
ptr_vector<th_solver> m_id2solver;
constraint* m_conflict = nullptr;
constraint* m_eq = nullptr;
constraint* m_lit = nullptr;
// proofs
bool m_proof_initialized = false;
ast_pp_util m_clause_visitor;
bool m_display_all_decls = false;
smt_proof_checker m_smt_proof_checker;
typedef std::pair<expr*, expr*> expr_pair;
literal_vector m_proof_literals;
svector<expr_pair> m_proof_eqs, m_proof_deqs, m_expr_pairs;
unsigned m_lit_head = 0, m_lit_tail = 0, m_cc_head = 0, m_cc_tail = 0;
unsigned m_eq_head = 0, m_eq_tail = 0, m_deq_head = 0, m_deq_tail = 0;
symbol m_euf = symbol("euf");
symbol m_smt = symbol("smt");
expr_ref_vector m_clause;
expr_ref_vector m_expr_args;
// internalization
bool visit(expr* e) override;
@ -128,7 +173,6 @@ namespace euf {
void add_not_distinct_axiom(app* e, euf::enode* const* args);
void axiomatize_basic(enode* n);
bool internalize_root(app* e, bool sign, ptr_vector<enode> const& args);
void ensure_merged_tf(euf::enode* n);
euf::enode* mk_true();
euf::enode* mk_false();
@ -162,7 +206,7 @@ namespace euf {
void validate_model(model& mdl);
// solving
void propagate_literals();
void propagate_literal(enode* n, enode* ante);
void propagate_th_eqs();
bool is_self_propagated(th_eq const& e);
void get_antecedents(literal l, constraint& j, literal_vector& r, bool probing);
@ -171,22 +215,26 @@ namespace euf {
// proofs
void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
void log_antecedents(literal l, literal_vector const& r);
void log_antecedents(literal l, literal_vector const& r, th_proof_hint* hint);
void log_justification(literal l, th_explain const& jst);
bool m_proof_initialized = false;
eq_proof_hint* mk_hint(symbol const& th, literal lit, literal_vector const& r);
void init_proof();
ast_pp_util m_clause_visitor;
bool m_display_all_decls = false;
void on_clause(unsigned n, literal const* lits, sat::status st) override;
void on_lemma(unsigned n, literal const* lits, sat::status st);
void on_proof(unsigned n, literal const* lits, sat::status st);
void on_check(unsigned n, literal const* lits, sat::status st);
void on_clause_eh(unsigned n, literal const* lits, sat::status st);
std::ostream& display_literals(std::ostream& out, unsigned n, sat::literal const* lits);
void display_assume(std::ostream& out, unsigned n, literal const* lits);
void display_redundant(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint);
void display_inferred(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint);
void display_deleted(std::ostream& out, unsigned n, literal const* lits);
std::ostream& display_hint(std::ostream& out, expr* proof_hint);
expr_ref status2proof_hint(sat::status st);
app_ref status2proof_hint(sat::status st);
// relevancy
bool is_propagated(sat::literal lit);
@ -203,7 +251,7 @@ namespace euf {
constraint& mk_constraint(constraint*& c, constraint::kind_t k);
constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); }
constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); }
constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); }
constraint& lit_constraint(enode* n);
// user propagator
void check_for_user_propagator() {
@ -217,7 +265,6 @@ namespace euf {
~solver() override {
if (m_conflict) dealloc(sat::constraint_base::mem2base_ptr(m_conflict));
if (m_eq) dealloc(sat::constraint_base::mem2base_ptr(m_eq));
if (m_lit) dealloc(sat::constraint_base::mem2base_ptr(m_lit));
m_trail.reset();
}
@ -280,6 +327,7 @@ namespace euf {
trail_stack& get_trail_stack() { return m_trail; }
void updt_params(params_ref const& p) override;
void set_solver(sat::solver* s) override { m_solver = s; use_drat(); }
void set_lookahead(sat::lookahead* s) override { m_lookahead = s; }
void init_search() override;
double get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const override;
@ -290,6 +338,7 @@ namespace euf {
bool should_research(sat::literal_vector const& core) override;
void add_assumptions(sat::literal_set& assumptions) override;
bool tracking_assumptions() override;
std::string reason_unknown() override { return m_reason_unknown; }
void propagate(literal lit, ext_justification_idx idx);
bool propagate(enode* a, enode* b, ext_justification_idx idx);
@ -305,8 +354,8 @@ namespace euf {
void get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) override;
void get_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing);
void add_antecedent(enode* a, enode* b);
void add_diseq_antecedent(ptr_vector<size_t>& ex, enode* a, enode* b);
void add_antecedent(bool probing, enode* a, enode* b);
void add_diseq_antecedent(ptr_vector<size_t>& ex, cc_justification* cc, enode* a, enode* b);
void add_explain(size_t* p) { m_explain.push_back(p); }
void reset_explain() { m_explain.reset(); }
void set_eliminated(bool_var v) override;
@ -341,7 +390,7 @@ namespace euf {
// proof
bool use_drat() { return s().get_config().m_drat && (init_proof(), true); }
bool use_drat() { return m_solver && s().get_config().m_drat && (init_proof(), true); }
sat::drat& get_drat() { return s().get_drat(); }
void set_tmp_bool_var(sat::bool_var b, expr* e);
@ -350,6 +399,37 @@ namespace euf {
void visit_expr(std::ostream& out, expr* e);
std::ostream& display_expr(std::ostream& out, expr* e);
void on_instantiation(unsigned n, sat::literal const* lits, unsigned k, euf::enode* const* bindings);
expr_ref_vector& expr_args() { m_expr_args.reset(); return m_expr_args; }
smt_proof_hint* mk_smt_hint(symbol const& n, literal_vector const& lits, enode_pair_vector const& eqs) {
return mk_smt_hint(n, lits.size(), lits.data(), eqs.size(), eqs.data());
}
smt_proof_hint* mk_smt_hint(symbol const& n, enode_pair_vector const& eqs) {
return mk_smt_hint(n, 0, nullptr, eqs.size(), eqs.data());
}
smt_proof_hint* mk_smt_hint(symbol const& n, literal_vector const& lits) {
return mk_smt_hint(n, lits.size(), lits.data(), 0, (expr_pair const*) nullptr);
}
smt_proof_hint* mk_smt_hint(symbol const& n, unsigned nl, literal const* lits, unsigned ne, expr_pair const* eqs, unsigned nd = 0, expr_pair const* deqs = nullptr);
smt_proof_hint* mk_smt_hint(symbol const& n, unsigned nl, literal const* lits, unsigned ne = 0, enode_pair const* eqs = nullptr);
smt_proof_hint* mk_smt_hint(symbol const& n, literal lit, unsigned ne, expr_pair const* eqs) { return mk_smt_hint(n, 1, &lit, ne, eqs); }
smt_proof_hint* mk_smt_hint(symbol const& n, literal lit) { return mk_smt_hint(n, 1, &lit, 0, (expr_pair const*)nullptr); }
smt_proof_hint* mk_smt_hint(symbol const& n, literal l1, literal l2) { literal ls[2] = {l1,l2}; return mk_smt_hint(n, 2, ls, 0, (expr_pair const*)nullptr); }
smt_proof_hint* mk_smt_hint(symbol const& n, literal lit, expr* a, expr* b) { expr_pair e(a, b); return mk_smt_hint(n, 1, &lit, 1, &e); }
smt_proof_hint* mk_smt_hint(symbol const& n, literal lit, enode* a, enode* b) { expr_pair e(a->get_expr(), b->get_expr()); return mk_smt_hint(n, 1, &lit, 1, &e); }
smt_proof_hint* mk_smt_prop_hint(symbol const& n, literal lit, expr* a, expr* b) { expr_pair e(a, b); return mk_smt_hint(n, 1, &lit, 0, nullptr, 1, &e); }
smt_proof_hint* mk_smt_prop_hint(symbol const& n, literal lit, enode* a, enode* b) { return mk_smt_prop_hint(n, lit, a->get_expr(), b->get_expr()); }
smt_proof_hint* mk_smt_hint(symbol const& n, enode* a, enode* b) { expr_pair e(a->get_expr(), b->get_expr()); return mk_smt_hint(n, 0, nullptr, 1, &e); }
smt_proof_hint* mk_smt_clause(symbol const& n, unsigned nl, literal const* lits);
th_proof_hint* mk_cc_proof_hint(sat::literal_vector const& ante, app* a, app* b);
th_proof_hint* mk_tc_proof_hint(sat::literal const* ternary_clause);
sat::status mk_tseitin_status(sat::literal a) { return mk_tseitin_status(1, &a); }
sat::status mk_tseitin_status(sat::literal a, sat::literal b);
sat::status mk_tseitin_status(unsigned n, sat::literal const* lits);
sat::status mk_distinct_status(sat::literal a) { return mk_distinct_status(1, &a); }
sat::status mk_distinct_status(sat::literal a, sat::literal b) { sat::literal lits[2] = { a, b }; return mk_distinct_status(2, lits); }
sat::status mk_distinct_status(sat::literal_vector const& lits) { return mk_distinct_status(lits.size(), lits.data()); }
sat::status mk_distinct_status(unsigned n, sat::literal const* lits);
scoped_ptr<std::ostream> m_proof_out;
// decompile
@ -359,8 +439,8 @@ namespace euf {
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override;
// internalize
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool learned) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
sat::literal mk_literal(expr* e);
void attach_th_var(enode* n, th_solver* th, theory_var v) { m_egraph.add_th_var(n, v, th->get_id()); }
void attach_node(euf::enode* n);
@ -368,8 +448,9 @@ namespace euf {
expr_ref mk_eq(euf::enode* n1, euf::enode* n2) { return mk_eq(n1->get_expr(), n2->get_expr()); }
euf::enode* e_internalize(expr* e);
euf::enode* mk_enode(expr* e, unsigned n, enode* const* args);
void set_bool_var2expr(sat::bool_var v, expr* e) { m_var_trail.push_back(v); m_bool_var2expr.setx(v, e, nullptr); }
expr* bool_var2expr(sat::bool_var v) const { return m_bool_var2expr.get(v, nullptr); }
expr_ref literal2expr(sat::literal lit) const { expr* e = bool_var2expr(lit.var()); return (e && lit.sign()) ? expr_ref(m.mk_not(e), m) : expr_ref(e, m); }
expr_ref literal2expr(sat::literal lit) const { expr* e = bool_var2expr(lit.var()); return (e && lit.sign()) ? expr_ref(mk_not(m, e), m) : expr_ref(e, m); }
unsigned generation() const { return m_generation; }
sat::literal attach_lit(sat::literal lit, expr* e);
@ -414,6 +495,11 @@ namespace euf {
// diagnostics
func_decl_ref_vector const& unhandled_functions() { return m_unhandled_functions; }
// clause tracing
void register_on_clause(
void* ctx,
user_propagator::on_clause_eh_t& on_clause);
// user propagator
void user_propagate_init(
void* ctx,

View file

@ -47,20 +47,18 @@ namespace fpa {
expr_ref solver::convert(expr* e) {
expr_ref res(m);
expr* ccnv;
TRACE("t_fpa", tout << "converting " << mk_ismt2_pp(e, m) << std::endl;);
TRACE("t_fpa", tout << "converting " << mk_ismt2_pp(e, m) << "\n";);
if (m_conversions.find(e, ccnv)) {
res = ccnv;
TRACE("t_fpa_detail", tout << "cached:" << std::endl;
tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl <<
mk_ismt2_pp(res, m) << std::endl;);
TRACE("t_fpa_detail", tout << "cached:" << "\n";
tout << mk_ismt2_pp(e, m) << "\n" << " -> " << "\n" << mk_ismt2_pp(res, m) << "\n";);
}
else {
res = m_rw.convert(m_th_rw, e);
TRACE("t_fpa_detail", tout << "converted; caching:" << std::endl;
tout << mk_ismt2_pp(e, m) << std::endl << " -> " << std::endl <<
mk_ismt2_pp(res, m) << std::endl;);
TRACE("t_fpa_detail", tout << "converted; caching:" << "\n";
tout << mk_ismt2_pp(e, m) << "\n" << " -> " << "\n" << mk_ismt2_pp(res, m) << "\n";);
m_conversions.insert(e, res);
m.inc_ref(e);
@ -97,9 +95,9 @@ namespace fpa {
TRACE("t_fpa", tout << "new theory var: " << mk_ismt2_pp(n->get_expr(), m) << " := " << v << "\n";);
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant))
if (!visit_rec(m, e, sign, root))
return sat::null_literal;
sat::literal lit = expr2literal(e);
if (sign)
@ -107,8 +105,8 @@ namespace fpa {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
visit_rec(m, e, false, false, redundant);
void solver::internalize(expr* e) {
visit_rec(m, e, false, false);
}
bool solver::visited(expr* e) {
@ -120,7 +118,7 @@ namespace fpa {
if (visited(e))
return true;
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
ctx.internalize(e);
return true;
}
m_stack.push_back(sat::eframe(e));
@ -257,26 +255,23 @@ namespace fpa {
}
void solver::ensure_equality_relation(theory_var x, theory_var y) {
fpa_util& fu = m_fpa_util;
enode* e_x = var2enode(x);
enode* e_y = var2enode(y);
TRACE("t_fpa", tout << "new eq: " << x << " = " << y << std::endl;
tout << mk_ismt2_pp(e_x->get_expr(), m) << std::endl << " = " << std::endl <<
mk_ismt2_pp(e_y->get_expr(), m) << std::endl;);
fpa_util& fu = m_fpa_util;
expr* xe = e_x->get_expr();
expr* ye = e_y->get_expr();
if (fu.is_bvwrap(xe) || fu.is_bvwrap(ye))
return;
TRACE("t_fpa", tout << "new eq: " << x << " = " << y << "\n";
tout << mk_ismt2_pp(xe, m) << "\n" << " = " << "\n" << mk_ismt2_pp(ye, m) << "\n";);
expr_ref xc = convert(xe);
expr_ref yc = convert(ye);
TRACE("t_fpa_detail", tout << "xc = " << mk_ismt2_pp(xc, m) << std::endl <<
"yc = " << mk_ismt2_pp(yc, m) << std::endl;);
TRACE("t_fpa_detail", tout << "xc = " << mk_ismt2_pp(xc, m) << "\n" <<
"yc = " << mk_ismt2_pp(yc, m) << "\n";);
expr_ref c(m);
@ -390,9 +385,9 @@ namespace fpa {
for (enode* n : ctx.get_egraph().nodes()) {
theory_var v = n->get_th_var(m_fpa_util.get_family_id());
if (v != -1) {
if (first) out << "fpa theory variables:" << std::endl;
if (first) out << "fpa theory variables:" << "\n";
out << v << " -> " <<
mk_ismt2_pp(n->get_expr(), m) << std::endl;
mk_ismt2_pp(n->get_expr(), m) << "\n";
first = false;
}
}
@ -400,24 +395,24 @@ namespace fpa {
if (first)
return out;
out << "bv theory variables:" << std::endl;
out << "bv theory variables:" << "\n";
for (enode* n : ctx.get_egraph().nodes()) {
theory_var v = n->get_th_var(m_bv_util.get_family_id());
if (v != -1) out << v << " -> " <<
mk_ismt2_pp(n->get_expr(), m) << std::endl;
mk_ismt2_pp(n->get_expr(), m) << "\n";
}
out << "arith theory variables:" << std::endl;
out << "arith theory variables:" << "\n";
for (enode* n : ctx.get_egraph().nodes()) {
theory_var v = n->get_th_var(m_arith_util.get_family_id());
if (v != -1) out << v << " -> " <<
mk_ismt2_pp(n->get_expr(), m) << std::endl;
mk_ismt2_pp(n->get_expr(), m) << "\n";
}
out << "equivalence classes:\n";
for (enode* n : ctx.get_egraph().nodes()) {
expr* e = n->get_expr();
out << n->get_root_id() << " --> " << mk_ismt2_pp(e, m) << std::endl;
out << n->get_root_id() << " --> " << mk_ismt2_pp(e, m) << "\n";
}
return out;
}

View file

@ -59,8 +59,8 @@ namespace fpa {
bool use_diseqs() const override { return true; }
void new_diseq_eh(euf::th_eq const& eq) override;
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
void apply_sort_cnstr(euf::enode* n, sort* s) override;
std::ostream& display(std::ostream& out) const override;

View file

@ -22,12 +22,11 @@ Author:
namespace pb {
void solver::internalize(expr* e, bool redundant) {
internalize(e, false, false, redundant);
void solver::internalize(expr* e) {
internalize(e, false, false);
}
literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
flet<bool> _redundant(m_is_redundant, redundant);
literal solver::internalize(expr* e, bool sign, bool root) {
if (m_pb.is_pb(e)) {
sat::literal lit = internalize_pb(e, sign, root);
if (m_ctx && !root && lit != sat::null_literal)
@ -84,7 +83,7 @@ namespace pb {
void solver::convert_pb_args(app* t, literal_vector& lits) {
for (expr* arg : *t) {
lits.push_back(si.internalize(arg, m_is_redundant));
lits.push_back(si.internalize(arg));
s().set_external(lits.back().var());
}
}
@ -114,13 +113,13 @@ namespace pb {
k1 += wl.first;
}
}
add_pb_ge(sat::null_bool_var, wlits, k1);
add_pb_ge(sat::null_bool_var, sign, wlits, k1);
return sat::null_literal;
}
else {
bool_var v = s().add_var(true);
literal lit(v, sign);
add_pb_ge(v, wlits, k.get_unsigned());
add_pb_ge(v, sign, wlits, k.get_unsigned());
TRACE("ba", tout << "root: " << root << " lit: " << lit << "\n";);
return lit;
}
@ -141,13 +140,13 @@ namespace pb {
k1 += wl.first;
}
}
add_pb_ge(sat::null_bool_var, wlits, k1);
add_pb_ge(sat::null_bool_var, sign, wlits, k1);
return sat::null_literal;
}
else {
sat::bool_var v = s().add_var(true);
sat::literal lit(v, sign);
add_pb_ge(v, wlits, k.get_unsigned());
add_pb_ge(v, sign, wlits, k.get_unsigned());
TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";);
return lit;
}
@ -161,14 +160,14 @@ namespace pb {
bool base_assert = (root && !sign && s().num_user_scopes() == 0);
bool_var v1 = base_assert ? sat::null_bool_var : s().add_var(true);
bool_var v2 = base_assert ? sat::null_bool_var : s().add_var(true);
add_pb_ge(v1, wlits, k.get_unsigned());
add_pb_ge(v1, false, wlits, k.get_unsigned());
k.neg();
for (wliteral& wl : wlits) {
wl.second.neg();
k += rational(wl.first);
}
check_unsigned(k);
add_pb_ge(v2, wlits, k.get_unsigned());
add_pb_ge(v2, false, wlits, k.get_unsigned());
if (base_assert) {
return sat::null_literal;
}

View file

@ -39,6 +39,8 @@ namespace pb {
m_max_sum(0) {
for (unsigned i = 0; i < size(); ++i) {
m_wlits[i] = wlits[i];
if (wlits[i].first > k)
m_wlits[i].first = k;
}
update_max_sum();
}
@ -47,9 +49,8 @@ namespace pb {
m_max_sum = 0;
for (unsigned i = 0; i < size(); ++i) {
m_wlits[i].first = std::min(k(), m_wlits[i].first);
if (m_max_sum + m_wlits[i].first < m_max_sum) {
if (m_max_sum + m_wlits[i].first < m_max_sum)
throw default_exception("addition of pb coefficients overflows");
}
m_max_sum += m_wlits[i].first;
}
}

View file

@ -690,13 +690,6 @@ namespace pb {
inc_coeff(consequent, offset);
process_antecedent(js.get_literal(), offset);
break;
case sat::justification::TERNARY:
inc_bound(offset);
SASSERT (consequent != sat::null_literal);
inc_coeff(consequent, offset);
process_antecedent(js.get_literal1(), offset);
process_antecedent(js.get_literal2(), offset);
break;
case sat::justification::CLAUSE: {
inc_bound(offset);
sat::clause & c = s().get_clause(js);
@ -1017,14 +1010,6 @@ namespace pb {
inc_coeff(consequent, 1);
process_antecedent(js.get_literal());
break;
case sat::justification::TERNARY:
SASSERT(consequent != sat::null_literal);
round_to_one(consequent.var());
inc_bound(1);
inc_coeff(consequent, 1);
process_antecedent(js.get_literal1());
process_antecedent(js.get_literal2());
break;
case sat::justification::CLAUSE: {
sat::clause & c = s().get_clause(js);
unsigned i = 0;
@ -1350,7 +1335,6 @@ namespace pb {
si(si), m_pb(m),
m_lookahead(nullptr),
m_constraint_id(0), m_ba(*this), m_sort(m_ba) {
TRACE("pb", tout << this << "\n";);
m_num_propagations_since_pop = 0;
}
@ -1428,6 +1412,7 @@ namespace pb {
}
if (!c->well_formed())
IF_VERBOSE(0, verbose_stream() << *c << "\n");
SASSERT(c->well_formed());
VERIFY(c->well_formed());
if (m_solver && m_solver->get_config().m_drat) {
auto * out = s().get_drat().out();
@ -1487,8 +1472,8 @@ namespace pb {
return p;
}
void solver::add_pb_ge(bool_var v, svector<wliteral> const& wlits, unsigned k) {
literal lit = v == sat::null_bool_var ? sat::null_literal : literal(v, false);
void solver::add_pb_ge(bool_var v, bool sign, svector<wliteral> const& wlits, unsigned k) {
literal lit = v == sat::null_bool_var ? sat::null_literal : literal(v, sign);
add_pb_ge(lit, wlits, k, m_is_redundant);
}
@ -2806,7 +2791,6 @@ namespace pb {
bool solver::subsumes(card& c1, card& c2, literal_vector & comp) {
if (c2.lit() != sat::null_literal) return false;
unsigned c2_exclusive = 0;
unsigned common = 0;
comp.reset();
for (literal l : c2) {
@ -2816,9 +2800,6 @@ namespace pb {
else if (is_visited(~l)) {
comp.push_back(l);
}
else {
++c2_exclusive;
}
}
unsigned c1_exclusive = c1.size() - common - comp.size();
@ -3420,16 +3401,13 @@ namespace pb {
unsigned slack = 0;
unsigned max_level = 0;
unsigned num_max_level = 0;
for (wliteral wl : m_wlits) {
if (value(wl.second) != l_false) ++slack;
unsigned level = lvl(wl.second);
if (level > max_level) {
max_level = level;
num_max_level = 1;
}
else if (max_level == level) {
++num_max_level;
}
}
if (m_overflow)
@ -3472,13 +3450,6 @@ namespace pb {
ineq.push(lit, offset);
ineq.push(js.get_literal(), offset);
break;
case sat::justification::TERNARY:
SASSERT(lit != sat::null_literal);
ineq.reset(offset);
ineq.push(lit, offset);
ineq.push(js.get_literal1(), offset);
ineq.push(js.get_literal2(), offset);
break;
case sat::justification::CLAUSE: {
ineq.reset(offset);
sat::clause & c = s().get_clause(js);

View file

@ -371,7 +371,7 @@ namespace pb {
~solver() override;
void set_lookahead(sat::lookahead* l) override { m_lookahead = l; }
void add_at_least(bool_var v, literal_vector const& lits, unsigned k);
void add_pb_ge(bool_var v, svector<wliteral> const& wlits, unsigned k);
void add_pb_ge(bool_var v, bool sign, svector<wliteral> const& wlits, unsigned k);
bool is_external(bool_var v) override;
bool propagated(literal l, sat::ext_constraint_idx idx) override;
@ -402,8 +402,8 @@ namespace pb {
bool is_blocked(literal l, sat::ext_constraint_idx idx) override;
bool check_model(sat::model const& m) const override;
literal internalize(expr* e, bool sign, bool root, bool redundant) override;
void internalize(expr* e, bool redundant) override;
literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override;
euf::th_solver* clone(euf::solver& ctx) override;

View file

@ -125,12 +125,13 @@ namespace q {
struct justification {
expr* m_lhs, * m_rhs;
bool m_sign;
unsigned m_generation;
unsigned m_num_ex;
size_t** m_explain;
clause& m_clause;
euf::enode* const* m_binding;
justification(lit const& l, clause& c, euf::enode* const* b, unsigned n, size_t** ev) :
m_lhs(l.lhs), m_rhs(l.rhs), m_sign(l.sign), m_num_ex(n), m_explain(ev), m_clause(c), m_binding(b) {}
justification(lit const& l, clause& c, euf::enode* const* b, unsigned generation, unsigned n, size_t** ev) :
m_lhs(l.lhs), m_rhs(l.rhs), m_sign(l.sign), m_generation(generation), m_num_ex(n), m_explain(ev), m_clause(c), m_binding(b) {}
sat::ext_constraint_idx to_index() const {
return sat::constraint_base::mem2base(this);
}

View file

@ -69,9 +69,12 @@ namespace q {
[&](euf::enode* n) {
m_mam->add_node(n, false);
};
ctx.get_egraph().set_on_merge(_on_merge);
if (!ctx.relevancy_enabled())
ctx.get_egraph().set_on_make(_on_make);
if (ctx.get_config().m_ematching) {
ctx.get_egraph().set_on_merge(_on_merge);
if (!ctx.relevancy_enabled())
ctx.get_egraph().set_on_make(_on_make);
}
m_mam = mam::mk(ctx, *this);
}
@ -104,7 +107,7 @@ namespace q {
* is created to ensure the justification trail is well-founded
* during conflict resolution.
*/
sat::ext_justification_idx ematch::mk_justification(unsigned idx, clause& c, euf::enode* const* b) {
sat::ext_justification_idx ematch::mk_justification(unsigned idx, unsigned generation, clause& c, euf::enode* const* b) {
void* mem = ctx.get_region().allocate(justification::get_obj_size());
sat::constraint_base::initialize(mem, &m_qs);
bool sign = false;
@ -113,21 +116,23 @@ namespace q {
if (idx != UINT_MAX)
lit = c[idx];
m_explain.reset();
m_explain_cc.reset();
ctx.get_egraph().begin_explain();
ctx.reset_explain();
euf::cc_justification* cc = ctx.use_drat() ? &m_explain_cc : nullptr;
for (auto const& [a, b] : m_evidence) {
SASSERT(a->get_root() == b->get_root() || ctx.get_egraph().are_diseq(a, b));
if (a->get_root() == b->get_root())
ctx.get_egraph().explain_eq<size_t>(m_explain, a, b);
ctx.get_egraph().explain_eq<size_t>(m_explain, cc, a, b);
else
ctx.add_diseq_antecedent(m_explain, a, b);
ctx.add_diseq_antecedent(m_explain, cc, a, b);
}
ctx.get_egraph().end_explain();
size_t** ev = static_cast<size_t**>(ctx.get_region().allocate(sizeof(size_t*) * m_explain.size()));
for (unsigned i = m_explain.size(); i-- > 0; )
ev[i] = m_explain[i];
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) justification(lit, c, b, m_explain.size(), ev);
auto* constraint = new (sat::constraint_base::ptr2mem(mem)) justification(lit, c, b, generation, m_explain.size(), ev);
return constraint->to_index();
}
@ -361,7 +366,7 @@ namespace q {
if (!is_owned)
binding = copy_nodes(c, binding);
auto j_idx = mk_justification(idx, c, binding);
auto j_idx = mk_justification(idx, max_generation, c, binding);
if (is_owned)
propagate(ev == l_false, idx, j_idx);
@ -381,11 +386,11 @@ namespace q {
sat::literal_vector lits;
lits.push_back(~j.m_clause.m_literal);
for (unsigned i = 0; i < j.m_clause.size(); ++i)
lits.push_back(instantiate(j.m_clause, j.m_binding, j.m_clause[i]));
lits.push_back(instantiate(j.m_clause, j.m_generation, j.m_binding, j.m_clause[i]));
m_qs.log_instantiation(lits, &j);
euf::th_proof_hint* ph = nullptr;
if (ctx.use_drat())
ph = q_proof_hint::mk(ctx, j.m_clause.size(), j.m_binding);
ph = q_proof_hint::mk(ctx, j.m_generation, lits, j.m_clause.num_decls(), j.m_binding);
m_qs.add_clause(lits, ph);
}
@ -412,15 +417,16 @@ namespace q {
void ematch::add_instantiation(clause& c, binding& b, sat::literal lit) {
m_evidence.reset();
ctx.propagate(lit, mk_justification(UINT_MAX, c, b.nodes()));
ctx.propagate(lit, mk_justification(UINT_MAX, b.m_max_generation, c, b.nodes()));
m_qs.log_instantiation(~c.m_literal, lit);
}
sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) {
sat::literal ematch::instantiate(clause& c, unsigned generation, euf::enode* const* binding, lit const& l) {
expr_ref_vector _binding(m);
for (unsigned i = 0; i < c.num_decls(); ++i)
_binding.push_back(binding[i]->get_expr());
var_subst subst(m);
euf::solver::scoped_generation sg(ctx, generation + 1);
auto sub = [&](expr* e) {
expr_ref r = subst(e, _binding);
//ctx.rewrite(r);

View file

@ -96,13 +96,14 @@ namespace q {
binding* alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top);
ptr_vector<size_t> m_explain;
sat::ext_justification_idx mk_justification(unsigned idx, clause& c, euf::enode* const* b);
euf::cc_justification m_explain_cc;
sat::ext_justification_idx mk_justification(unsigned idx, unsigned generation, clause& c, euf::enode* const* b);
void ensure_ground_enodes(expr* e);
void ensure_ground_enodes(clause const& c);
void instantiate(binding& b);
sat::literal instantiate(clause& c, euf::enode* const* binding, lit const& l);
sat::literal instantiate(clause& c, unsigned generation, euf::enode* const* binding, lit const& l);
// register as callback into egraph.
void on_merge(euf::enode* root, euf::enode* other);

View file

@ -124,11 +124,9 @@ namespace q {
std::swap(t, s);
}
unsigned sz = evidence.size();
unsigned count = 0;
for (euf::enode* t1 : euf::enode_class(tn)) {
if (!t1->is_cgr())
continue;
++count;
expr* t2 = t1->get_expr();
if ((c = compare_rec(n, binding, s, t2, evidence), c != l_undef)) {
evidence.push_back(euf::enode_pair(t1, tn));

View file

@ -3797,7 +3797,6 @@ namespace q {
}
void rematch(bool use_irrelevant) override {
unsigned lbl = 0;
for (auto * t : m_trees) {
if (t) {
m_interpreter.init(t);
@ -3807,7 +3806,6 @@ namespace q {
m_interpreter.execute_core(t, curr);
}
}
++lbl;
}
}

View file

@ -70,19 +70,10 @@ namespace q {
m_max_cex += ctx.get_config().m_mbqi_max_cexs;
for (auto const& [qlit, fml, inst, generation] : m_instantiations) {
euf::solver::scoped_generation sg(ctx, generation + 1);
sat::literal lit = ctx.mk_literal(fml);
euf::th_proof_hint* ph = nullptr;
if (!inst.empty()) {
ph = q_proof_hint::mk(ctx, inst.size(), inst.data());
sat::literal_vector lits;
lits.push_back(~qlit);
lits.push_back(~lit);
m_qs.add_clause(lits, ph);
}
else {
m_qs.add_clause(~qlit, ~lit);
}
m_qs.log_instantiation(~qlit, ~lit);
sat::literal lit = ~ctx.mk_literal(fml);
auto* ph = ctx.use_drat()? q_proof_hint::mk(ctx, generation, ~qlit, lit, inst.size(), inst.data()) : nullptr;
m_qs.add_clause(~qlit, lit, ph);
m_qs.log_instantiation(~qlit, lit);
}
m_instantiations.reset();
if (result != l_true)
@ -116,12 +107,14 @@ namespace q {
if (ctx.values2root().find(e, n) && n->class_generation() <= generation_min)
eqs.push_back(m.mk_eq(sk, e));
}
m_solver->assert_expr(mk_or(eqs));
assert_expr(mk_or(eqs));
}
expr_ref mbqi::replace_model_value(expr* e) {
auto const& v2r = ctx.values2root();
euf::enode* r = nullptr;
if (m.is_bool(e))
return expr_ref(e, m);
if (v2r.find(e, r))
return choose_term(r);
if (is_app(e) && to_app(e)->get_num_args() > 0) {
@ -132,7 +125,11 @@ namespace q {
}
if (m.is_model_value(e))
return expr_ref(m.mk_model_value(0, e->get_sort()), m);
return expr_ref(e, m);
expr_ref e1 = m_model->unfold_as_array(e);
if (e1 == e)
return e1;
return replace_model_value(e1);
}
expr_ref mbqi::choose_term(euf::enode* r) {
@ -144,7 +141,7 @@ namespace q {
r = n;
}
else if (n->generation() == gen) {
if ((m_qs.random() % ++count) == 0)
if ((m_qs.random() % ++count) == 0 && has_quantifiers(n->get_expr()))
r = n;
}
if (count > m_max_choose_candidates)
@ -171,9 +168,11 @@ namespace q {
while (true) {
::solver::scoped_push _sp(*m_solver);
add_universe_restriction(*qb);
m_solver->assert_expr(qb->mbody);
assert_expr(qb->mbody);
++m_stats.m_num_checks;
IF_VERBOSE(2, verbose_stream() << "(mbqi.check)\n");
lbool r = m_solver->check_sat(0, nullptr);
IF_VERBOSE(2, verbose_stream() << "(mbqi.check " << r << ")\n");
if (r == l_undef)
return r;
if (r == l_true) {
@ -198,6 +197,7 @@ namespace q {
expr_ref_vector eqs(m);
add_domain_bounds(mdl, qb);
auto proj = solver_project(mdl, qb, eqs, false);
CTRACE("q", !proj, tout << "could not project " << qb.mbody << " " << eqs << "\n" << mdl);
if (!proj)
return false;
add_instantiation(q, proj);
@ -214,14 +214,17 @@ namespace q {
add_domain_eqs(mdl0, qb);
for (; i < m_max_cex; ++i) {
++m_stats.m_num_checks;
if (l_true != m_solver->check_sat(0, nullptr))
IF_VERBOSE(2, verbose_stream() << "(mbqi.check)\n");
lbool r = m_solver->check_sat(0, nullptr);
IF_VERBOSE(2, verbose_stream() << "(mbqi.check " << r << ")\n");
if (l_true != r)
break;
m_solver->get_model(mdl1);
auto proj = solver_project(*mdl1, qb, eqs, true);
if (!proj)
break;
add_instantiation(q, proj);
m_solver->assert_expr(m.mk_not(mk_and(eqs)));
assert_expr(m.mk_not(mk_and(eqs)));
}
return i > 0;
}
@ -240,22 +243,22 @@ namespace q {
}
expr_ref_vector mbqi::extract_binding(quantifier* q) {
if (!m_defs.empty()) {
expr_safe_replace sub(m);
for (unsigned i = m_defs.size(); i-- > 0; ) {
sub(m_defs[i].term);
sub.insert(m_defs[i].var, m_defs[i].term);
}
q_body* qb = q2body(q);
expr_ref_vector inst(m);
for (expr* v : qb->vars) {
expr_ref t(m);
sub(v, t);
inst.push_back(t);
}
return inst;
SASSERT(!ctx.use_drat() || !m_defs.empty());
if (m_defs.empty())
return expr_ref_vector(m);
expr_safe_replace sub(m);
for (unsigned i = m_defs.size(); i-- > 0; ) {
sub(m_defs[i].term);
sub.insert(m_defs[i].var, m_defs[i].term);
}
return expr_ref_vector(m);
q_body* qb = q2body(q);
expr_ref_vector inst(m);
for (expr* v : qb->vars) {
expr_ref t(m);
sub(v, t);
inst.push_back(t);
}
return inst;
}
@ -339,8 +342,10 @@ namespace q {
fmls.append(qb.domain_eqs);
eliminate_nested_vars(fmls, qb);
for (expr* e : fmls)
if (!m_model->is_true(e))
if (!m_model->is_true(e)) {
TRACE("q", tout << "not true: " << mk_pp(e, m) << " := " << (*m_model)(e) << "\n");
return expr_ref(nullptr, m);
}
mbp::project_plugin proj(m);
proj.extract_literals(*m_model, vars, fmls);
fmls_extracted = true;
@ -348,24 +353,27 @@ namespace q {
if (!p)
continue;
if (ctx.use_drat()) {
if (!p->project(*m_model, vars, fmls, m_defs))
return expr_ref(m);
if (!p->project(*m_model, vars, fmls, m_defs))
return expr_ref(m);
}
else if (!(*p)(*m_model, vars, fmls))
else if (!(*p)(*m_model, vars, fmls)) {
TRACE("q", tout << "theory projection failed\n");
return expr_ref(m);
}
}
for (app* v : vars) {
expr_ref term(m);
expr_ref val = (*m_model)(v);
val = m_model->unfold_as_array(val);
term = replace_model_value(val);
TRACE("euf", tout << "replaced model value " << term << "\nfrom\n" << val << "\n");
rep.insert(v, term);
if (ctx.use_drat())
m_defs.push_back(mbp::def(expr_ref(v, m), term));
eqs.push_back(m.mk_eq(v, val));
}
rep(fmls);
TRACE("q", tout << "generated formulas\n" << fmls << "\ngenerated eqs:\n" << eqs << "\n";);
TRACE("q", tout << "generated formulas\n" << fmls << "\ngenerated eqs:\n" << eqs << "\n";
for (auto const& [v,t] : m_defs) tout << v << " := " << t << "\n");
return mk_and(fmls);
}
@ -389,7 +397,7 @@ namespace q {
if (!m_model->eval_expr(bounds, mbounds, true))
return;
mbounds = subst(mbounds, qb.vars);
m_solver->assert_expr(mbounds);
assert_expr(mbounds);
qb.domain_eqs.push_back(vbounds);
}
@ -408,10 +416,13 @@ namespace q {
auto* n = nodes[i];
expr* e = n->get_expr();
expr* val = ctx.node2value(n);
if (val && e->get_sort() == srt && !m.is_value(e) && !visited.is_marked(val)) {
if (val && e->get_sort() == srt &&
!m.is_value(e) &&
!visited.is_marked(val)) {
visited.mark(val);
expr_ref value = replace_model_value(val);
veqs.push_back(m.mk_eq(v, e));
meqs.push_back(m.mk_eq(v, val));
meqs.push_back(m.mk_eq(v, value));
--bound;
}
}
@ -419,19 +430,29 @@ namespace q {
continue;
expr_ref meq = mk_or(meqs);
expr_ref veq = mk_or(veqs);
m_solver->assert_expr(meq);
assert_expr(meq);
qb.domain_eqs.push_back(veq);
}
}
void mbqi::assert_expr(expr* e) {
expr_ref _e(e, m);
TRACE("q", tout << _e << "\n");
m_solver->assert_expr(e);
}
/*
* Add bounds to sub-terms under uninterpreted functions for projection.
*/
void mbqi::add_domain_bounds(model& mdl, q_body& qb) {
qb.domain_eqs.reset();
m_model->reset_eval_cache();
for (app* v : qb.vars)
m_model->register_decl(v->get_decl(), mdl(v));
{
model::scoped_model_completion _sc(mdl, true);
for (app* v : qb.vars)
m_model->register_decl(v->get_decl(), mdl(v));
}
ctx.model_updated(m_model);
if (qb.var_args.empty())
return;
@ -440,7 +461,8 @@ namespace q {
expr_ref _term = subst(t, qb.vars);
app_ref term(to_app(_term), m);
expr_ref value = (*m_model)(term->get_arg(idx));
m_model_fixer.invert_arg(term, idx, value, qb.domain_eqs);
if (m.is_value(value))
m_model_fixer.invert_arg(term, idx, value, qb.domain_eqs);
}
}
@ -470,6 +492,7 @@ namespace q {
expr_ref _term = subst(e, qb.vars);
app_ref term(to_app(_term), m);
expr_ref value = (*m_model)(term);
value = replace_model_value(value);
expr* s = m_model_fixer.invert_app(term, value);
rep.insert(term, s);
expr_ref eq(m.mk_eq(term, s), m);
@ -542,6 +565,14 @@ namespace q {
body = subst(q_flat->get_expr(), binding);
if (is_forall(q))
body = ::mk_not(m, body);
if (ctx.use_drat()) {
m_defs.reset();
for (unsigned i = 0; i < binding.size(); ++i) {
expr_ref v(qb.vars.get(i), m);
expr_ref t(binding.get(i), m);
m_defs.push_back(mbp::def(v, t));
}
}
add_instantiation(q, body);
++num_bindings;
}
@ -573,6 +604,7 @@ namespace q {
binding.reset();
auto const& nodes = ctx.get_egraph().nodes();
m_model->reset_eval_cache();
model::scoped_model_completion _sc(*m_model, true);
for (unsigned j = 0; j < offsets.size(); ++j) {
unsigned offset = offsets[j];
binding.push_back(nodes[offset]->get_expr());
@ -609,7 +641,7 @@ namespace q {
void mbqi::init_solver() {
if (!m_solver)
m_solver = mk_smt2_solver(m, m_no_drat_params);
m_solver = mk_smt2_solver(m, m_no_drat_params, symbol::null);
}
void mbqi::init_search() {

View file

@ -93,6 +93,7 @@ namespace q {
void extract_free_vars(quantifier* q, q_body& qb);
void init_model();
void init_solver();
void assert_expr(expr* e);
mbp::project_plugin* get_plugin(app* var);
void add_plugin(mbp::project_plugin* p);
void add_instantiation(quantifier* q, expr_ref& proj);

View file

@ -253,7 +253,7 @@ namespace q {
euf::enode* r = nullptr;
auto& v2r = ctx.values2root();
TRACE("q",
tout << "invert-app " << mk_pp(t, m) << " = " << mk_pp(value, m) << "\n";
tout << "invert-app " << mk_pp(t, m) << " =\n" << mk_pp(value, m) << "\n";
if (v2r.find(value, r))
tout << "inverse " << mk_pp(r->get_expr(), m) << "\n";
/*ctx.display(tout); */

View file

@ -23,7 +23,7 @@ Author:
#include "sat/smt/q_solver.h"
#include "sat/smt/euf_solver.h"
#include "sat/smt/sat_th.h"
#include "qe/lite/qe_lite.h"
#include "qe/lite/qe_lite_tactic.h"
#include <iostream>
@ -53,7 +53,7 @@ namespace q {
if (!m_flat.find(q, q_flat)) {
if (expand(q)) {
for (expr* e : m_expanded) {
sat::literal lit = ctx.internalize(e, l.sign(), false, false);
sat::literal lit = ctx.internalize(e, l.sign(), false);
add_clause(~l, lit);
}
return;
@ -62,7 +62,7 @@ namespace q {
}
if (is_ground(q_flat->get_expr())) {
auto lit = ctx.internalize(q_flat->get_expr(), l.sign(), false, false);
auto lit = ctx.internalize(q_flat->get_expr(), l.sign(), false);
add_clause(~l, lit);
}
else {
@ -163,7 +163,7 @@ namespace q {
m_mbqi.init_search();
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool learned) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
SASSERT(is_forall(e) || is_exists(e));
sat::bool_var v = ctx.get_si().add_bool_var(e);
sat::literal lit = ctx.attach_lit(sat::literal(v, false), e);
@ -364,36 +364,45 @@ namespace q {
}
}
q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned n, euf::enode* const* bindings) {
auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n));
q_proof_hint* ph = new (mem) q_proof_hint();
ph->m_num_bindings = n;
q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned generation, sat::literal_vector const& lits, unsigned n, euf::enode* const* bindings) {
SASSERT(n > 0);
auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n, lits.size()));
q_proof_hint* ph = new (mem) q_proof_hint(generation, n, lits.size());
for (unsigned i = 0; i < n; ++i)
ph->m_bindings[i] = bindings[i]->get_expr();
for (unsigned i = 0; i < lits.size(); ++i)
ph->m_literals[i] = lits[i];
return ph;
}
q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned n, expr* const* bindings) {
auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n));
q_proof_hint* ph = new (mem) q_proof_hint();
ph->m_num_bindings = n;
q_proof_hint* q_proof_hint::mk(euf::solver& s, unsigned generation, sat::literal l1, sat::literal l2, unsigned n, expr* const* bindings) {
SASSERT(n > 0);
auto* mem = s.get_region().allocate(q_proof_hint::get_obj_size(n, 2));
q_proof_hint* ph = new (mem) q_proof_hint(generation, n, 2);
for (unsigned i = 0; i < n; ++i)
ph->m_bindings[i] = bindings[i];
ph->m_literals[0] = l1;
ph->m_literals[1] = l2;
return ph;
}
expr* q_proof_hint::get_hint(euf::solver& s) const {
ast_manager& m = s.get_manager();
expr_ref_vector args(m);
sort_ref_vector sorts(m);
for (unsigned i = 0; i < m_num_bindings; ++i) {
args.push_back(m_bindings[i]);
sorts.push_back(args.back()->get_sort());
}
expr_ref binding(m);
arith_util a(m);
expr_ref gen(a.mk_int(m_generation), m);
expr* gens[1] = { gen.get() };
sort* range = m.mk_proof_sort();
func_decl* d = m.mk_func_decl(symbol("inst"), args.size(), sorts.data(), range);
expr* r = m.mk_app(d, args);
return r;
for (unsigned i = 0; i < m_num_bindings; ++i)
args.push_back(m_bindings[i]);
binding = m.mk_app(symbol("bind"), args.size(), args.data(), range);
args.reset();
for (unsigned i = 0; i < m_num_literals; ++i)
args.push_back(s.literal2expr(~m_literals[i]));
args.push_back(binding);
args.push_back(m.mk_app(symbol("gen"), 1, gens, range));
return m.mk_app(symbol("inst"), args.size(), args.data(), range);
}
}

View file

@ -30,12 +30,21 @@ namespace euf {
namespace q {
struct q_proof_hint : public euf::th_proof_hint {
unsigned m_num_bindings;
expr* m_bindings[0];
q_proof_hint() {}
static size_t get_obj_size(unsigned num_bindings) { return sizeof(q_proof_hint) + num_bindings*sizeof(expr*); }
static q_proof_hint* mk(euf::solver& s, unsigned n, euf::enode* const* bindings);
static q_proof_hint* mk(euf::solver& s, unsigned n, expr* const* bindings);
unsigned m_generation;
unsigned m_num_bindings;
unsigned m_num_literals;
sat::literal* m_literals;
expr* m_bindings[0];
q_proof_hint(unsigned g, unsigned b, unsigned l) {
m_generation = g;
m_num_bindings = b;
m_num_literals = l;
m_literals = reinterpret_cast<sat::literal*>(m_bindings + m_num_bindings);
}
static size_t get_obj_size(unsigned num_bindings, unsigned num_lits) { return sizeof(q_proof_hint) + num_bindings*sizeof(expr*) + num_lits*sizeof(sat::literal); }
static q_proof_hint* mk(euf::solver& s, unsigned generation, sat::literal_vector const& lits, unsigned n, euf::enode* const* bindings);
static q_proof_hint* mk(euf::solver& s, unsigned generation, sat::literal l1, sat::literal l2, unsigned n, expr* const* bindings);
expr* get_hint(euf::solver& s) const override;
};
@ -86,8 +95,8 @@ namespace q {
void collect_statistics(statistics& st) const override;
euf::th_solver* clone(euf::solver& ctx) override;
bool unit_propagate() override;
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override { internalize(e, false, false, redundant); }
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override { internalize(e, false, false); }
euf::theory_var mk_var(euf::enode* n) override;
void init_search() override;
void finalize_model(model& mdl) override;

View file

@ -0,0 +1,66 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
q_theory_checker.cpp
Abstract:
Plugin for checking quantifier instantiations
Author:
Nikolaj Bjorner (nbjorner) 2022-10-07
--*/
#include "ast/rewriter/var_subst.h"
#include "sat/smt/q_theory_checker.h"
#include "sat/smt/q_solver.h"
namespace q {
expr_ref_vector theory_checker::clause(app* jst) {
expr_ref_vector result(m);
for (expr* arg : *jst)
if (m.is_bool(arg))
result.push_back(mk_not(m, arg));
return result;
}
expr_ref_vector theory_checker::binding(app* jst) {
expr_ref_vector result(m);
for (expr* arg : *jst)
if (is_bind(arg)) {
result.append(to_app(arg)->get_num_args(), to_app(arg)->get_args());
break;
}
return result;
}
bool theory_checker::vc(app* jst, expr_ref_vector const& clause0, expr_ref_vector& v) {
expr* q = nullptr;
if (!is_inst(jst))
return false;
auto clause1 = clause(jst);
SASSERT(clause1.size() >= 2);
VERIFY(m.is_not(clause1.get(0), q) && is_forall(q));
auto inst = binding(jst);
expr_ref qi = instantiate(m, to_quantifier(q), inst.begin());
clause1[0] = m.mk_not(qi);
v.reset();
v.append(clause1);
return qi == clause1.get(1);
}
bool theory_checker::is_inst(expr* jst) {
return is_app(jst) && to_app(jst)->get_name() == m_inst && m.mk_proof_sort() == jst->get_sort();
}
bool theory_checker::is_bind(expr* e) {
return is_app(e) && to_app(e)->get_name() == m_bind && m.mk_proof_sort() == e->get_sort();
}
}

View file

@ -0,0 +1,58 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
q_theory_checker.h
Abstract:
Plugin for checking quantifier instantiations
Author:
Nikolaj Bjorner (nbjorner) 2022-10-07
--*/
#pragma once
#include "util/obj_pair_set.h"
#include "ast/ast_trail.h"
#include "ast/ast_util.h"
#include "sat/smt/euf_proof_checker.h"
#include <iostream>
namespace q {
class theory_checker : public euf::theory_checker_plugin {
ast_manager& m;
symbol m_inst;
symbol m_bind;
expr_ref_vector binding(app* jst);
bool is_inst(expr* jst);
bool is_bind(expr* e);
public:
theory_checker(ast_manager& m):
m(m),
m_inst("inst"),
m_bind("bind") {
}
expr_ref_vector clause(app* jst) override;
bool check(app* jst) override { return false; }
void register_plugins(euf::theory_checker& pc) override {
pc.register_plugin(symbol("inst"), this);
}
bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) override;
};
}

View file

@ -232,10 +232,10 @@ namespace recfun {
ctx.push(push_back_vector<scoped_ptr_vector<propagation_item>>(m_propagation_queue));
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
force_push();
SASSERT(m.is_bool(e));
if (!visit_rec(m, e, sign, root, redundant)) {
if (!visit_rec(m, e, sign, root)) {
TRACE("array", tout << mk_pp(e, m) << "\n";);
return sat::null_literal;
}
@ -245,9 +245,9 @@ namespace recfun {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
void solver::internalize(expr* e) {
force_push();
visit_rec(m, e, false, false, redundant);
visit_rec(m, e, false, false);
}
bool solver::visited(expr* e) {
@ -259,7 +259,7 @@ namespace recfun {
if (visited(e))
return true;
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
ctx.internalize(e);
return true;
}
m_stack.push_back(sat::eframe(e));

View file

@ -101,8 +101,8 @@ namespace recfun {
void collect_statistics(statistics& st) const override;
euf::th_solver* clone(euf::solver& ctx) override;
bool unit_propagate() override;
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
bool add_dep(euf::enode* n, top_sort<euf::enode>& dep) override;
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
bool is_shared(euf::theory_var v) const override { return true; }

View file

@ -23,9 +23,10 @@ namespace sat {
public:
virtual ~sat_internalizer() = default;
virtual bool is_bool_op(expr* e) const = 0;
virtual literal internalize(expr* e, bool learned) = 0;
virtual literal internalize(expr* e) = 0;
virtual bool_var to_bool_var(expr* e) = 0;
virtual bool_var add_bool_var(expr* e) = 0;
virtual bool is_cached(app* t, literal l) const = 0;
virtual void cache(app* t, literal l) = 0;
virtual void uncache(literal l) = 0;
virtual void push() = 0;

View file

@ -21,9 +21,8 @@ Author:
namespace euf {
bool th_internalizer::visit_rec(ast_manager& m, expr* a, bool sign, bool root, bool redundant) {
bool th_internalizer::visit_rec(ast_manager& m, expr* a, bool sign, bool root) {
IF_VERBOSE(110, verbose_stream() << "internalize: " << mk_pp(a, m) << "\n");
flet<bool> _is_learned(m_is_redundant, redundant);
svector<sat::eframe>::scoped_stack _sc(m_stack);
unsigned sz = m_stack.size();
visit(a);
@ -125,13 +124,11 @@ namespace euf {
pop_core(n);
}
sat::status th_euf_solver::mk_status(th_proof_hint const* ps) {
return sat::status::th(m_is_redundant, get_id(), ps);
}
bool th_euf_solver::add_unit(sat::literal lit) {
bool th_euf_solver::add_unit(sat::literal lit, th_proof_hint const* ps) {
if (ctx.use_drat() && !ps)
ps = ctx.mk_smt_clause(name(), 1, &lit);
bool was_true = is_true(lit);
ctx.s().add_clause(1, &lit, mk_status());
ctx.s().add_clause(1, &lit, sat::status::th(false, get_id(), ps));
ctx.add_root(lit);
return !was_true;
}
@ -143,33 +140,31 @@ namespace euf {
is_new = true;
return is_new;
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b) {
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, th_proof_hint const* ph) {
sat::literal lits[2] = { a, b };
return add_clause(2, lits);
return add_clause(2, lits, ph);
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps) {
sat::literal lits[2] = { a, b };
return add_clause(2, lits, ps);
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, th_proof_hint const* ps) {
sat::literal lits[3] = { a, b, c };
return add_clause(3, lits);
return add_clause(3, lits, ps);
}
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) {
bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d, th_proof_hint const* ps) {
sat::literal lits[4] = { a, b, c, d };
return add_clause(4, lits);
return add_clause(4, lits, ps);
}
bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps) {
bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps, bool is_redundant) {
if (ctx.use_drat() && !ps)
ps = ctx.mk_smt_clause(name(), n, lits);
bool was_true = false;
for (unsigned i = 0; i < n; ++i)
was_true |= is_true(lits[i]);
ctx.add_root(n, lits);
s().add_clause(n, lits, mk_status(ps));
s().add_clause(n, lits, sat::status::th(is_redundant, get_id(), ps));
return !was_true;
}
@ -254,46 +249,46 @@ namespace euf {
return new (sat::constraint_base::ptr2mem(mem)) th_explain(n_lits, lits, n_eqs, eqs, c, enode_pair(x, y), pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* pma) {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr, pma);
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* ph) {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr, ph);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma) {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* ph) {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, ph);
}
th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma) {
return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* ph) {
return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, ph);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y) {
return mk(th, 1, &lit, 0, nullptr, sat::null_literal, x, y);
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y, th_proof_hint const* ph) {
return mk(th, 1, &lit, 0, nullptr, sat::null_literal, x, y, ph);
}
th_explain* th_explain::conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs) {
return conflict(th, lits.size(), lits.data(), eqs.size(), eqs.data());
th_explain* th_explain::conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, th_proof_hint const* ph) {
return conflict(th, lits.size(), lits.data(), eqs.size(), eqs.data(), ph);
}
th_explain* th_explain::conflict(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs) {
return mk(th, n_lits, lits, n_eqs, eqs, sat::null_literal, nullptr, nullptr);
th_explain* th_explain::conflict(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, th_proof_hint const* ph) {
return mk(th, n_lits, lits, n_eqs, eqs, sat::null_literal, nullptr, nullptr, ph);
}
th_explain* th_explain::conflict(th_euf_solver& th, enode_pair_vector const& eqs) {
return conflict(th, 0, nullptr, eqs.size(), eqs.data());
th_explain* th_explain::conflict(th_euf_solver& th, enode_pair_vector const& eqs, th_proof_hint const* ph) {
return conflict(th, 0, nullptr, eqs.size(), eqs.data(), ph);
}
th_explain* th_explain::conflict(th_euf_solver& th, sat::literal lit) {
return conflict(th, 1, &lit, 0, nullptr);
th_explain* th_explain::conflict(th_euf_solver& th, sat::literal lit, th_proof_hint const* ph) {
return conflict(th, 1, &lit, 0, nullptr, ph);
}
th_explain* th_explain::conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y) {
th_explain* th_explain::conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y, th_proof_hint const* ph) {
enode_pair eq(x, y);
return conflict(th, 1, &lit, 1, &eq);
return conflict(th, 1, &lit, 1, &eq, ph);
}
th_explain* th_explain::conflict(th_euf_solver& th, euf::enode* x, euf::enode* y) {
th_explain* th_explain::conflict(th_euf_solver& th, euf::enode* x, euf::enode* y, th_proof_hint const* ph) {
enode_pair eq(x, y);
return conflict(th, 0, nullptr, 1, &eq);
return conflict(th, 0, nullptr, 1, &eq, ph);
}
std::ostream& th_explain::display(std::ostream& out) const {

View file

@ -30,9 +30,8 @@ namespace euf {
protected:
euf::enode_vector m_args;
svector<sat::eframe> m_stack;
bool m_is_redundant{ false };
bool visit_rec(ast_manager& m, expr* e, bool sign, bool root, bool redundant);
bool visit_rec(ast_manager& m, expr* e, bool sign, bool root);
virtual bool visit(expr* e) { return false; }
virtual bool visited(expr* e) { return false; }
@ -41,9 +40,9 @@ namespace euf {
public:
virtual ~th_internalizer() = default;
virtual sat::literal internalize(expr* e, bool sign, bool root, bool redundant) = 0;
virtual sat::literal internalize(expr* e, bool sign, bool root) = 0;
virtual void internalize(expr* e, bool redundant) = 0;
virtual void internalize(expr* e) = 0;
/**
@ -135,7 +134,7 @@ namespace euf {
virtual bool is_beta_redex(euf::enode* p, euf::enode* n) const { return false; }
sat::status status() const { return sat::status::th(m_is_redundant, get_id()); }
sat::status status() const { return sat::status::th(false, get_id()); }
};
@ -155,19 +154,18 @@ namespace euf {
sat::literal expr2literal(expr* e) const;
region& get_region();
sat::status mk_status(th_proof_hint const* ps = nullptr);
bool add_unit(sat::literal lit);
bool add_unit(sat::literal lit, th_proof_hint const* ps = nullptr);
bool add_units(sat::literal_vector const& lits);
bool add_clause(sat::literal lit) { return add_unit(lit); }
bool add_clause(sat::literal a, sat::literal b);
bool add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps);
bool add_clause(sat::literal a, sat::literal b, sat::literal c);
bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
bool add_clause(sat::literal lit, th_proof_hint const* ps = nullptr) { return add_unit(lit, ps); }
bool add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps = nullptr);
bool add_clause(sat::literal a, sat::literal b, sat::literal c, th_proof_hint const* ps = nullptr);
bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d, th_proof_hint const* ps = nullptr);
bool add_clause(sat::literal_vector const& lits, th_proof_hint const* ps = nullptr) { return add_clause(lits.size(), lits.data(), ps); }
bool add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps = nullptr);
bool add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps, bool is_redundant = false);
void add_equiv(sat::literal a, sat::literal b);
void add_equiv_and(sat::literal a, sat::literal_vector const& bs);
bool add_redundant(sat::literal_vector const& lits, th_proof_hint const* ps) { return add_clause(lits.size(), lits.data(), ps, true); }
bool add_redundant(unsigned n, sat::literal* lits, th_proof_hint const* ps);
bool is_true(sat::literal lit);
@ -234,21 +232,21 @@ namespace euf {
sat::literal* m_literals;
enode_pair* m_eqs;
static size_t get_obj_size(unsigned num_lits, unsigned num_eqs);
th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, th_proof_hint const* pma = nullptr);
static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* pma = nullptr);
th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, th_proof_hint const* ph = nullptr);
static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* ph = nullptr);
public:
static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs);
static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits) { return conflict(th, lits.size(), lits.data(), 0, nullptr); }
static th_explain* conflict(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs);
static th_explain* conflict(th_euf_solver& th, enode_pair_vector const& eqs);
static th_explain* conflict(th_euf_solver& th, sat::literal lit);
static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma = nullptr);
static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, th_proof_hint const* ph = nullptr);
static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, th_proof_hint const* ph = nullptr) { return conflict(th, lits.size(), lits.data(), 0, nullptr, ph); }
static th_explain* conflict(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, th_proof_hint const* ph = nullptr);
static th_explain* conflict(th_euf_solver& th, enode_pair_vector const& eqs, th_proof_hint const* ph = nullptr);
static th_explain* conflict(th_euf_solver& th, sat::literal lit, th_proof_hint const* ph = nullptr);
static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y, th_proof_hint const* ph = nullptr);
static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y, th_proof_hint const* ph = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y, th_proof_hint const* ph = nullptr);
static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* ph = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* ph = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* ph = nullptr);
sat::ext_constraint_idx to_index() const {
return sat::constraint_base::mem2base(this);

View file

@ -0,0 +1,256 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
tseitin_theory_checker.cpp
Abstract:
Plugin for checking quantifier instantiations
Author:
Nikolaj Bjorner (nbjorner) 2022-10-07
TODOs:
- handle distinct
- handle other internalization from euf_internalize
- equiv should be modulo commutativity (the E-graph indexes expressions modulo commutativity of top-level operator)
- should we log rules for root clauses too? Root clauses should follow from input.
They may be simplified using Tseitin transformation. For example, (and a b) is clausified into
two clauses a, b.
- Tesitin checking could also be performed by depth-bounded SAT (e.g., using BDDs)
--*/
#include "ast/ast_pp.h"
#include "sat/smt/tseitin_theory_checker.h"
namespace tseitin {
expr_ref_vector theory_checker::clause(app* jst) {
expr_ref_vector result(m);
result.append(jst->get_num_args(), jst->get_args());
return result;
}
bool theory_checker::check(app* jst) {
expr* main_expr = nullptr;
unsigned max_depth = 0;
expr* a, * x, * y, * z, * u, * v;
for (expr* arg : *jst) {
unsigned arg_depth = get_depth(arg);
if (arg_depth > max_depth) {
main_expr = arg;
max_depth = arg_depth;
}
if (arg_depth == max_depth && m.is_not(main_expr)) {
if (m.is_not(arg, x) && m.is_not(main_expr, y) &&
is_app(x) && is_app(y) &&
to_app(x)->get_num_args() < to_app(y)->get_num_args())
continue;
main_expr = arg;
}
}
if (!main_expr)
return false;
// (or (and a b) (not a) (not b))
// (or (and (not a) b) a (not b))
if (m.is_and(main_expr)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
for (expr* arg : *to_app(main_expr))
if (!is_complement(arg))
return false;
return true;
}
// (or (or a b) (not a))
if (m.is_or(main_expr)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
for (expr* arg : *to_app(main_expr))
if (is_complement(arg))
return true;
return false;
}
// (or (= a b) a b)
// (or (= a b) (not a) (not b))
// (or (= (not a) b) a (not b))
if (m.is_eq(main_expr, x, y) && m.is_bool(x)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_marked(x) && is_marked(y))
return true;
if (is_complement(x) && is_complement(y))
return true;
}
if (m.is_eq(main_expr, x, y) && m.is_ite(x, z, u, v)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_marked(z) && equiv(y, v))
return true;
if (is_complement(z) && equiv(y, u))
return true;
}
// (or (if a b c) (not b) (not c))
// (or (if a b c) a (not c))
// (or (if a b c) (not a) (not b))
if (m.is_ite(main_expr, x, y, z) && m.is_bool(z)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_marked(x) && is_complement(z))
return true;
if (is_complement(x) && is_complement(y))
return true;
if (is_complement(y) && is_complement(z))
return true;
IF_VERBOSE(0, verbose_stream() << mk_pp(main_expr, m) << "\n");
}
// (or (=> a b) a)
// (or (=> a b) (not b))
if (m.is_implies(main_expr, x, y)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_marked(x))
return true;
if (is_complement(y))
return true;
}
// (or (xor a b c d) a b (not c) (not d))
if (m.is_xor(main_expr)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
int parity = 0;
for (expr* arg : *to_app(main_expr))
if (is_marked(arg))
parity++;
else if (is_complement(arg))
parity--;
if ((parity % 2) == 0)
return true;
}
if (m.is_not(main_expr, a)) {
// (or (not a) a')
for (expr* arg : *jst)
if (equiv(a, arg))
return true;
// (or (not (and a b)) a)
if (m.is_and(a)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
mark(arg);
for (expr* arg : *to_app(a))
if (is_marked(arg))
return true;
}
// (or (not (or a b) a b))
if (m.is_or(a)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
mark(arg);
for (expr* arg : *to_app(a))
if (!is_marked(arg))
return false;
return true;
}
// (or (not (= a b) (not a) b)
if (m.is_eq(a, x, y) && m.is_bool(x)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_marked(x) && is_complement(y))
return true;
if (is_marked(y) && is_complement(x))
return true;
}
// (or (not (if a b c)) (not a) b)
// (or (not (if a b c)) a c)
if (m.is_ite(a, x, y, z) && m.is_bool(z)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_complement(x) && is_marked(y))
return true;
if (is_marked(x) && is_marked(z))
return true;
if (is_marked(y) && is_marked(z))
return true;
}
// (or (not (=> a b)) b (not a))
if (m.is_implies(a, x, y)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
if (is_complement(x) && is_marked(y))
return true;
}
// (or (not (xor a b c d)) a b c (not d))
if (m.is_xor(a)) {
scoped_mark sm(*this);
for (expr* arg : *jst)
complement_mark(arg);
int parity = 1;
for (expr* arg : *to_app(main_expr))
if (is_marked(arg))
parity++;
else if (is_complement(arg))
parity--;
if ((parity % 2) == 0)
return true;
}
IF_VERBOSE(0, verbose_stream() << "miss " << mk_pp(main_expr, m) << "\n");
}
return false;
}
bool theory_checker::equiv(expr* a, expr* b) {
if (a == b)
return true;
if (!is_app(a) || !is_app(b))
return false;
if (to_app(a)->get_decl() != to_app(b)->get_decl())
return false;
if (!to_app(a)->get_decl()->is_commutative())
return false;
if (to_app(a)->get_num_args() != 2)
return false;
return to_app(a)->get_arg(0) == to_app(b)->get_arg(1) &&
to_app(a)->get_arg(1) == to_app(b)->get_arg(0);
}
}

View file

@ -0,0 +1,74 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
tseitin_theory_checker.h
Abstract:
Plugin for checking tseitin internalization
Author:
Nikolaj Bjorner (nbjorner) 2022-10-07
--*/
#pragma once
#include "util/obj_pair_set.h"
#include "ast/ast_trail.h"
#include "ast/ast_util.h"
#include "sat/smt/euf_proof_checker.h"
#include <iostream>
namespace tseitin {
class theory_checker : public euf::theory_checker_plugin {
ast_manager& m;
expr_fast_mark1 m_mark;
expr_fast_mark2 m_nmark;
bool equiv(expr* a, expr* b);
void mark(expr* a) { m_mark.mark(a); }
bool is_marked(expr* a) { return m_mark.is_marked(a); }
void nmark(expr* a) { m_nmark.mark(a); }
bool is_nmarked(expr* a) { return m_nmark.is_marked(a); }
void complement_mark(expr* a) {
m_mark.mark(a);
if (m.is_not(a, a))
m_nmark.mark(a);
}
bool is_complement(expr* a) {
if (m.is_not(a, a))
return is_marked(a);
else
return is_nmarked(a);
}
struct scoped_mark {
theory_checker& pc;
scoped_mark(theory_checker& pc): pc(pc) {}
~scoped_mark() { pc.m_mark.reset(); pc.m_nmark.reset(); }
};
public:
theory_checker(ast_manager& m):
m(m) {
}
expr_ref_vector clause(app* jst) override;
bool check(app* jst) override;
void register_plugins(euf::theory_checker& pc) override {
pc.register_plugin(symbol("tseitin"), this);
}
};
}

View file

@ -30,7 +30,7 @@ namespace user_solver {
void solver::add_expr(expr* e) {
force_push();
ctx.internalize(e, false);
ctx.internalize(e);
euf::enode* n = expr2enode(e);
if (is_attached_to_var(n))
return;
@ -63,7 +63,7 @@ namespace user_solver {
return;
}
force_push();
ctx.internalize(e, false);
ctx.internalize(e);
m_next_split_expr = e;
m_next_split_idx = idx;
m_next_split_phase = phase;
@ -162,7 +162,7 @@ namespace user_solver {
}
void solver::propagate_consequence(prop_info const& prop) {
sat::literal lit = ctx.internalize(prop.m_conseq, false, false, true);
sat::literal lit = ctx.internalize(prop.m_conseq, false, false);
if (s().value(lit) != l_true) {
s().assign(lit, mk_justification(m_qhead));
++m_stats.m_num_propagations;
@ -207,7 +207,7 @@ namespace user_solver {
for (unsigned id : prop.m_ids)
r.append(m_id2justification[id]);
for (auto const& p : prop.m_eqs)
ctx.add_antecedent(expr2enode(p.first), expr2enode(p.second));
ctx.add_antecedent(probing, expr2enode(p.first), expr2enode(p.second));
}
/*
@ -250,8 +250,8 @@ namespace user_solver {
return result;
}
sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) {
if (!visit_rec(m, e, sign, root, redundant)) {
sat::literal solver::internalize(expr* e, bool sign, bool root) {
if (!visit_rec(m, e, sign, root)) {
TRACE("array", tout << mk_pp(e, m) << "\n";);
return sat::null_literal;
}
@ -263,15 +263,15 @@ namespace user_solver {
return lit;
}
void solver::internalize(expr* e, bool redundant) {
visit_rec(m, e, false, false, redundant);
void solver::internalize(expr* e) {
visit_rec(m, e, false, false);
}
bool solver::visit(expr* e) {
if (visited(e))
return true;
if (!is_app(e) || to_app(e)->get_family_id() != get_id()) {
ctx.internalize(e, m_is_redundant);
ctx.internalize(e);
return true;
}
m_stack.push_back(sat::eframe(e));

View file

@ -154,8 +154,8 @@ namespace user_solver {
bool unit_propagate() override;
void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r, bool probing) override;
void collect_statistics(statistics& st) const override;
sat::literal internalize(expr* e, bool sign, bool root, bool learned) override;
void internalize(expr* e, bool redundant) override;
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
std::ostream& display(std::ostream& out) const override;
std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override;
std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override;

View file

@ -23,9 +23,9 @@ namespace xr {
th_solver* clone(euf::solver& ctx) override;
sat::literal internalize(expr* e, bool sign, bool root, bool redundant) override { UNREACHABLE(); return sat::null_literal; }
sat::literal internalize(expr* e, bool sign, bool root) override { UNREACHABLE(); return sat::null_literal; }
void internalize(expr* e, bool redundant) override { UNREACHABLE(); }
void internalize(expr* e) override { UNREACHABLE(); }
void asserted(sat::literal l) override;

View file

@ -37,7 +37,7 @@ Notes:
#include "model/model_evaluator.h"
#include "model/model_v2_pp.h"
#include "tactic/tactic.h"
#include "tactic/generic_model_converter.h"
#include "ast/converters/generic_model_converter.h"
#include "sat/sat_cut_simplifier.h"
#include "sat/sat_drat.h"
#include "sat/tactic/goal2sat.h"
@ -68,15 +68,14 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::solver_core & m_solver;
atom2bool_var & m_map;
dep2asm_map & m_dep2asm;
obj_map<expr, sat::bool_var>* m_expr2var_replay { nullptr };
obj_map<expr, sat::bool_var>* m_expr2var_replay = nullptr;
bool m_ite_extra;
unsigned long long m_max_memory;
expr_ref_vector m_trail;
func_decl_ref_vector m_unhandled_funs;
bool m_default_external;
bool m_euf { false };
bool m_is_redundant { false };
bool m_top_level { false };
bool m_euf = false;
bool m_top_level = false;
sat::literal_vector aig_lits;
imp(ast_manager & _m, params_ref const & p, sat::solver_core & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external):
@ -100,7 +99,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat_params sp(p);
m_ite_extra = p.get_bool("ite_extra", true);
m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX));
m_euf = sp.euf();
m_euf = sp.euf() || sp.smt();
}
void throw_op_not_handled(std::string const& s) {
@ -108,8 +107,32 @@ struct goal2sat::imp : public sat::sat_internalizer {
throw tactic_exception(std::move(s0));
}
sat::status mk_status() const {
return sat::status::th(m_is_redundant, m.get_basic_family_id());
symbol m_tseitin = symbol("tseitin");
euf::th_proof_hint* mk_tseitin(unsigned n, sat::literal const* lits) {
if (m_euf && ensure_euf()->use_drat())
return ensure_euf()->mk_smt_hint(m_tseitin, n, lits);
return nullptr;
}
euf::th_proof_hint* mk_tseitin(sat::literal a, sat::literal b) {
if (m_euf && ensure_euf()->use_drat()) {
sat::literal lits[2] = { a, b };
return ensure_euf()->mk_smt_hint(m_tseitin, 2, lits);
}
return nullptr;
}
euf::th_proof_hint* mk_tseitin(sat::literal a, sat::literal b, sat::literal c) {
if (m_euf && ensure_euf()->use_drat()) {
sat::literal lits[3] = { a, b, c };
return ensure_euf()->mk_smt_hint(m_tseitin, 3, lits);
}
return nullptr;
}
sat::status mk_status(euf::th_proof_hint* ph = nullptr) const {
return sat::status::th(false, m.get_basic_family_id(), ph);
}
bool relevancy_enabled() {
@ -118,48 +141,44 @@ struct goal2sat::imp : public sat::sat_internalizer {
bool top_level_relevant() {
return m_top_level && relevancy_enabled();
}
void mk_clause(sat::literal l) {
mk_clause(1, &l);
}
}
void mk_clause(sat::literal l1, sat::literal l2) {
void mk_clause(sat::literal l1, sat::literal l2, euf::th_proof_hint* ph) {
sat::literal lits[2] = { l1, l2 };
mk_clause(2, lits);
mk_clause(2, lits, ph);
}
void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) {
void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3, euf::th_proof_hint* ph) {
sat::literal lits[3] = { l1, l2, l3 };
mk_clause(3, lits);
mk_clause(3, lits, ph);
}
void mk_clause(unsigned n, sat::literal * lits) {
void mk_clause(unsigned n, sat::literal * lits, euf::th_proof_hint* ph) {
TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < n; i++) tout << lits[i] << " "; tout << "\n";);
if (relevancy_enabled())
ensure_euf()->add_aux(n, lits);
m_solver.add_clause(n, lits, mk_status());
m_solver.add_clause(n, lits, mk_status(ph));
}
void mk_root_clause(sat::literal l) {
mk_root_clause(1, &l);
}
void mk_root_clause(sat::literal l1, sat::literal l2) {
void mk_root_clause(sat::literal l1, sat::literal l2, euf::th_proof_hint* ph = nullptr) {
sat::literal lits[2] = { l1, l2 };
mk_root_clause(2, lits);
mk_root_clause(2, lits, ph);
}
void mk_root_clause(sat::literal l1, sat::literal l2, sat::literal l3) {
void mk_root_clause(sat::literal l1, sat::literal l2, sat::literal l3, euf::th_proof_hint* ph = nullptr) {
sat::literal lits[3] = { l1, l2, l3 };
mk_root_clause(3, lits);
mk_root_clause(3, lits, ph);
}
void mk_root_clause(unsigned n, sat::literal * lits) {
void mk_root_clause(unsigned n, sat::literal * lits, euf::th_proof_hint* ph = nullptr) {
TRACE("goal2sat", tout << "mk_root_clause: "; for (unsigned i = 0; i < n; i++) tout << lits[i] << " "; tout << "\n";);
if (relevancy_enabled())
ensure_euf()->add_root(n, lits);
m_solver.add_clause(n, lits, m_is_redundant ? mk_status() : sat::status::input());
m_solver.add_clause(n, lits, ph ? mk_status(ph) : sat::status::input());
}
sat::bool_var add_var(bool is_ext, expr* n) {
@ -167,6 +186,8 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (m_expr2var_replay && m_expr2var_replay->find(n, v))
return v;
v = m_solver.add_var(is_ext);
if (!is_ext && m_euf && ensure_euf()->use_drat())
ensure_euf()->set_bool_var2expr(v, n);
return v;
}
@ -254,6 +275,13 @@ struct goal2sat::imp : public sat::sat_internalizer {
m_lit2app.insert(l.index(), t);
m_cache_trail.push_back(t);
}
bool is_cached(app* t, sat::literal l) const override {
if (!m_app2lit.contains(t))
return false;
SASSERT(m_app2lit[t] == l);
return true;
}
void convert_atom(expr * t, bool root, bool sign) {
SASSERT(m.is_bool(t));
@ -262,11 +290,15 @@ struct goal2sat::imp : public sat::sat_internalizer {
if (v == sat::null_bool_var) {
if (m.is_true(t)) {
sat::literal tt = sat::literal(mk_bool_var(t), false);
if (m_euf && ensure_euf()->use_drat())
ensure_euf()->set_bool_var2expr(tt.var(), t);
mk_root_clause(tt);
l = sign ? ~tt : tt;
}
else if (m.is_false(t)) {
sat::literal ff = sat::literal(mk_bool_var(t), false);
if (m_euf && ensure_euf()->use_drat())
ensure_euf()->set_bool_var2expr(ff.var(), t);
mk_root_clause(~ff);
l = sign ? ~ff : ff;
}
@ -401,7 +433,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
cache(t, l);
sat::literal * lits = m_result_stack.end() - num;
for (unsigned i = 0; i < num; i++)
mk_clause(~lits[i], l);
mk_clause(~lits[i], l, mk_tseitin(~lits[i], l));
m_result_stack.push_back(~l);
lits = m_result_stack.end() - num - 1;
@ -411,7 +443,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
// remark: mk_clause may perform destructive updated to lits.
// I have to execute it after the binary mk_clause above.
mk_clause(num+1, lits);
mk_clause(num+1, lits, mk_tseitin(num+1, lits));
if (aig())
aig()->add_or(l, num, aig_lits.data());
@ -454,7 +486,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
// l => /\ lits
for (unsigned i = 0; i < num; i++) {
mk_clause(~l, lits[i]);
mk_clause(~l, lits[i], mk_tseitin(~l, lits[i]));
}
// /\ lits => l
for (unsigned i = 0; i < num; ++i) {
@ -466,7 +498,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
aig_lits.reset();
aig_lits.append(num, lits);
}
mk_clause(num+1, lits);
mk_clause(num+1, lits, mk_tseitin(num+1, lits));
if (aig()) {
aig()->add_and(l, num, aig_lits.data());
}
@ -476,7 +508,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
m_result_stack.shrink(old_sz);
m_result_stack.push_back(l);
TRACE("goal2sat", tout << m_result_stack << "\n";);
}
}
@ -504,13 +535,13 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::bool_var k = add_var(false, n);
sat::literal l(k, false);
cache(n, l);
mk_clause(~l, ~c, t);
mk_clause(~l, c, e);
mk_clause(l, ~c, ~t);
mk_clause(l, c, ~e);
mk_clause(~l, ~c, t, mk_tseitin(~l, ~c, t));
mk_clause(~l, c, e, mk_tseitin(~l, c, e));
mk_clause(l, ~c, ~t, mk_tseitin(l, ~c, ~t));
mk_clause(l, c, ~e, mk_tseitin(l, c, ~e));
if (m_ite_extra) {
mk_clause(~t, ~e, l);
mk_clause(t, e, ~l);
mk_clause(~t, ~e, l, mk_tseitin(~t, ~e, l));
mk_clause(t, e, ~l, mk_tseitin(t, e, ~l));
}
if (aig()) aig()->add_ite(l, c, t, e);
if (sign)
@ -537,8 +568,8 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::literal l(k, false);
cache(t, l);
// l <=> ~lit
mk_clause(lit, l);
mk_clause(~lit, ~l);
mk_clause(lit, l, mk_tseitin(lit, l));
mk_clause(~lit, ~l, mk_tseitin(~lit, ~l));
if (sign)
l.neg();
m_result_stack.push_back(l);
@ -569,9 +600,9 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::literal l(k, false);
cache(t, l);
// l <=> (l1 => l2)
mk_clause(~l, ~l1, l2);
mk_clause(l1, l);
mk_clause(~l2, l);
mk_clause(~l, ~l1, l2, mk_tseitin(~l, ~l1, l2));
mk_clause(l1, l, mk_tseitin(l1, l));
mk_clause(~l2, l, mk_tseitin(~l2, l));
if (sign)
l.neg();
m_result_stack.push_back(l);
@ -607,10 +638,10 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::literal l(k, false);
if (m.is_xor(t))
l1.neg();
mk_clause(~l, l1, ~l2);
mk_clause(~l, ~l1, l2);
mk_clause(l, l1, l2);
mk_clause(l, ~l1, ~l2);
mk_clause(~l, l1, ~l2, mk_tseitin(~l, l1, ~l2));
mk_clause(~l, ~l1, l2, mk_tseitin(~l, ~l1, l2));
mk_clause(l, l1, l2, mk_tseitin(l, l1, l2));
mk_clause(l, ~l1, ~l2, mk_tseitin(l, ~l1, ~l2));
if (aig()) aig()->add_iff(l, l1, l2);
cache(t, l);
@ -656,7 +687,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
sat::literal lit;
{
flet<bool> _top(m_top_level, false);
lit = euf->internalize(e, sign, root, m_is_redundant);
lit = euf->internalize(e, sign, root);
}
if (lit == sat::null_literal)
return;
@ -679,7 +710,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
th = dynamic_cast<euf::th_solver*>(ext);
SASSERT(th);
}
auto lit = th->internalize(t, sign, root, m_is_redundant);
auto lit = th->internalize(t, sign, root);
m_result_stack.shrink(m_result_stack.size() - t->get_num_args());
if (lit == sat::null_literal)
return;
@ -748,12 +779,11 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
};
void process(expr* n, bool is_root, bool redundant) {
void process(expr* n, bool is_root) {
TRACE("goal2sat", tout << "process-begin " << mk_bounded_pp(n, m, 2)
<< " root: " << is_root
<< " result-stack: " << m_result_stack.size()
<< " frame-stack: " << m_frame_stack.size() << "\n";);
flet<bool> _is_redundant(m_is_redundant, redundant);
scoped_stack _sc(*this, is_root);
unsigned sz = m_frame_stack.size();
if (visit(n, is_root, false))
@ -804,14 +834,14 @@ struct goal2sat::imp : public sat::sat_internalizer {
<< " result-stack: " << m_result_stack.size() << "\n";);
}
sat::literal internalize(expr* n, bool redundant) override {
sat::literal internalize(expr* n) override {
bool is_not = m.is_not(n, n);
flet<bool> _top(m_top_level, false);
unsigned sz = m_result_stack.size();
(void)sz;
SASSERT(n->get_ref_count() > 0);
TRACE("goal2sat", tout << "internalize " << mk_bounded_pp(n, m, 2) << "\n";);
process(n, false, redundant);
process(n, false);
SASSERT(m_result_stack.size() == sz + 1);
sat::literal result = m_result_stack.back();
TRACE("goal2sat", tout << "done internalize " << result << " " << mk_bounded_pp(n, m, 2) << "\n";);
@ -857,7 +887,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
flet<bool> _top(m_top_level, true);
VERIFY(m_result_stack.empty());
TRACE("goal2sat", tout << "assert: " << mk_bounded_pp(n, m, 3) << "\n";);
process(n, true, m_is_redundant);
process(n, true);
CTRACE("goal2sat", !m_result_stack.empty(), tout << m_result_stack << "\n";);
SASSERT(m_result_stack.empty());
}
@ -918,6 +948,8 @@ struct goal2sat::imp : public sat::sat_internalizer {
expr_ref f(m), d_new(m);
ptr_vector<expr> deps;
expr_ref_vector fmls(m);
if (m_euf)
ensure_euf();
for (unsigned idx = 0; idx < size; idx++) {
f = g.form(idx);
// Add assumptions.
@ -1028,16 +1060,21 @@ void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver_core
(*m_imp)(g);
}
void goal2sat::operator()(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external) {
init(m, p, t, map, dep2asm, default_external);
void goal2sat::operator()(unsigned n, expr* const* fmls) {
SASSERT(m_imp);
(*m_imp)(n, fmls);
}
void goal2sat::assumptions(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external) {
init(m, p, t, map, dep2asm, default_external);
void goal2sat::assumptions(unsigned n, expr* const* fmls) {
SASSERT(m_imp);
m_imp->assumptions(n, fmls);
}
sat::literal goal2sat::internalize(expr* a) {
SASSERT(m_imp);
return m_imp->internalize(a);
}
void goal2sat::get_interpreted_funs(func_decl_ref_vector& funs) {
if (m_imp)

View file

@ -67,12 +67,13 @@ public:
*/
void operator()(goal const & g, params_ref const & p, sat::solver_core & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false);
void operator()(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external = false);
void operator()(unsigned n, expr* const* fmls);
void init(ast_manager& m, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external);
void assumptions(unsigned n, expr* const* fmls);
void assumptions(ast_manager& m, unsigned n, expr* const* fmls, params_ref const & p, sat::solver_core & t, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external = false);
sat::literal internalize(expr* a);
void get_interpreted_funs(func_decl_ref_vector& funs);

View file

@ -37,7 +37,7 @@ Notes:
#include "model/model_evaluator.h"
#include "model/model_v2_pp.h"
#include "tactic/tactic.h"
#include "tactic/generic_model_converter.h"
#include "ast/converters/generic_model_converter.h"
#include "sat/sat_cut_simplifier.h"
#include "sat/sat_drat.h"
#include "sat/tactic/sat2goal.h"

View file

@ -31,7 +31,7 @@ Notes:
#include "tactic/goal.h"
#include "sat/sat_model_converter.h"
#include "sat/sat_solver.h"
#include "tactic/generic_model_converter.h"
#include "ast/converters/generic_model_converter.h"
#include "sat/smt/atom2bool_var.h"
class sat2goal {

View file

@ -74,6 +74,8 @@ class sat_tactic : public tactic {
TRACE("sat", tout << "result of checking: " << r << " ";
if (r == l_undef) tout << m_solver->get_reason_unknown(); tout << "\n";
if (m_goal2sat.has_interpreted_funs()) tout << "has interpreted\n";);
if (r == l_undef)
g->set_reason_unknown(m_solver->get_reason_unknown());
if (r == l_false) {
expr_dependency * lcore = nullptr;
if (produce_core) {

View file

@ -13,7 +13,36 @@ Author:
Leonardo (leonardo) 2011-10-26
Notes:
Tactic Documentation:
## Tactic sat
### Short Description
Try to solve goal using a SAT solver
## Tactic sat-preprocess
### Short Description
Apply SAT solver preprocessing procedures (bounded resolution, Boolean constant propagation, 2-SAT, subsumption, subsumption resolution).
### Example
```z3
(declare-const a Bool)
(declare-const b Bool)
(declare-const c Bool)
(declare-const d Bool)
(declare-const e Bool)
(declare-const f Bool)
(declare-fun p (Bool) Bool)
(assert (=> a b))
(assert (=> b c))
(assert a)
(assert (not c))
(apply sat-preprocess)
```
--*/
#pragma once