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:
commit
20b5455d08
669 changed files with 26145 additions and 20652 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"; }
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
357
src/sat/sat_proof_trim.cpp
Normal 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
91
src/sat/sat_proof_trim.h
Normal 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();
|
||||
|
||||
};
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
z3_add_component(sat_solver
|
||||
SOURCES
|
||||
inc_sat_solver.cpp
|
||||
sat_smt_solver.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
aig_tactic
|
||||
arith_tactics
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
705
src/sat/sat_solver/sat_smt_solver.cpp
Normal file
705
src/sat/sat_solver/sat_smt_solver.cpp
Normal 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);
|
||||
}
|
||||
|
25
src/sat/sat_solver/sat_smt_solver.h
Normal file
25
src/sat/sat_solver/sat_smt_solver.h
Normal 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);
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
75
src/sat/smt/bv_theory_checker.cpp
Normal file
75
src/sat/smt/bv_theory_checker.cpp
Normal 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; }
|
||||
|
||||
}
|
95
src/sat/smt/bv_theory_checker.h
Normal file
95
src/sat/smt/bv_theory_checker.h
Normal 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
50
src/sat/smt/distinct_theory_checker.h
Normal file
50
src/sat/smt/distinct_theory_checker.h
Normal 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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); */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
66
src/sat/smt/q_theory_checker.cpp
Normal file
66
src/sat/smt/q_theory_checker.cpp
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
58
src/sat/smt/q_theory_checker.h
Normal file
58
src/sat/smt/q_theory_checker.h
Normal 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
256
src/sat/smt/tseitin_theory_checker.cpp
Normal file
256
src/sat/smt/tseitin_theory_checker.cpp
Normal 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);
|
||||
}
|
||||
}
|
74
src/sat/smt/tseitin_theory_checker.h
Normal file
74
src/sat/smt/tseitin_theory_checker.h
Normal 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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue