3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

save state of arith

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2023-06-03 11:52:15 -07:00
parent ffd4323573
commit a7b3dae262
16 changed files with 267 additions and 37 deletions

View file

@ -25,6 +25,7 @@ z3_add_component(ast
expr_abstract.cpp
expr_functors.cpp
expr_map.cpp
expr_polarities.cpp
expr_stat.cpp
expr_substitution.cpp
for_each_ast.cpp

View file

@ -0,0 +1,86 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
expr_polarities.cpp
Abstract:
Extract polarities of expressions.
Author:
Nikolaj Bjorner (nbjorner) 2023-06-01
--*/
#pragma once
#include "ast/expr_polarities.h"
void expr_polarities::push() {
m_fresh_lim.push_back(m_fresh.size());
m_trail_lim.push_back(m_trail.size());
}
void expr_polarities::pop(unsigned n) {
unsigned sz = m_fresh_lim[m_fresh_lim.size() - n];
for (unsigned i = m_fresh_lim.size(); --i > sz; ) {
auto const & [e, p] = m_fresh[i];
if (p)
m_pos.mark(e, false);
else
m_neg.mark(e, false);
}
m_fresh.shrink(sz);
m_fresh_lim.shrink(m_fresh_lim.size() - n);
sz = m_trail_lim[m_trail_lim.size() - n];
m_trail.shrink(sz);
m_trail_lim.shrink(m_trail_lim.size() - n);
}
void expr_polarities::add(expr* e) {
if (m_pos.is_marked(e))
return;
m_trail.push_back(e);
buffer<std::pair<bool, expr*>> frames;
frames.push_back({true, e});
while (!frames.empty()) {
auto [p, e] = frames.back();
frames.pop_back();
if (p) {
if (m_pos.is_marked(e))
continue;
m_pos.mark(e, true);
}
else {
if (m_neg.is_marked(e))
continue;
m_neg.mark(e, true);
}
m_fresh.push_back({e, p});
if (m.is_and(e) || m.is_or(e)) {
for (expr* arg : *to_app(e))
frames.push_back({p, arg});
}
else if (m.is_not(e, e)) {
frames.push_back({!p, e});
}
else if (m.is_implies(e)) {
frames.push_back({!p, to_app(e)->get_arg(0)});
frames.push_back({p, to_app(e)->get_arg(1)});
}
else if (is_app(e)) {
for (expr* arg : *to_app(e)) {
frames.push_back({true, arg});
frames.push_back({false, arg});
}
}
else if (is_quantifier(e))
frames.push_back({p, to_quantifier(e)->get_expr()});
}
}

52
src/ast/expr_polarities.h Normal file
View file

@ -0,0 +1,52 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
expr_polarities.h
Abstract:
Extract polarities of expressions.
Author:
Nikolaj Bjorner (nbjorner) 2023-06-01
--*/
#pragma once
#include "ast/ast.h"
class expr_polarities {
ast_manager & m;
expr_ref_vector m_trail;
expr_mark m_pos, m_neg;
vector<std::pair<expr*, bool>> m_fresh;
unsigned_vector m_fresh_lim, m_trail_lim;
public:
expr_polarities(ast_manager & m) : m(m), m_trail(m) {}
void push();
void pop(unsigned n);
// add expressions to annotate with polarities
void add(expr* e);
void add(expr_ref_vector const& es) {
for (expr* e : es)
add(e);
}
bool has_negative(expr* e) const {
return m_neg.is_marked(e);
}
bool has_positive(expr* e) const {
return m_pos.is_marked(e);
}
};

View file

@ -51,7 +51,7 @@ class int_solver {
public:
patcher(int_solver& lia);
bool should_apply() const { return true; }
lia_move operator()() { return patch_basic_columns(); }
lia_move operator()() { return patch_nbasic_columns(); }
void patch_nbasic_column(unsigned j);
bool patch_basic_column(unsigned v, row_cell<mpq> const& c);
void patch_basic_column(unsigned j);

View file

@ -46,9 +46,17 @@ namespace arith {
del_bounds(0);
}
void solver::asserted(literal l) {
void solver::asserted(literal lit) {
force_push();
m_asserted.push_back(l);
expr* e = ctx.bool_var2expr(lit.var());
if (lit.sign() && !ctx.is_neg(e)) {
//verbose_stream() << "not negative " << mk_pp(e, m) << "\n";
}
else if (!lit.sign() && !ctx.is_pos(e)) {
//verbose_stream() << "not positive " << mk_pp(e, m) << "\n";
}
else
m_asserted.push_back(lit);
}
euf::th_solver* solver::clone(euf::solver& dst_ctx) {

View file

@ -34,6 +34,10 @@ Notes:
namespace euf {
void solver::add_polarities(expr* f) {
m_polarities.add(f);
}
void solver::internalize(expr* e) {
if (get_enode(e))
return;

View file

@ -48,6 +48,7 @@ namespace euf {
m_unhandled_functions(m),
m_to_m(&m),
m_to_si(&si),
m_polarities(m),
m_clause_visitor(m),
m_smt_proof_checker(m, p),
m_clause(m),
@ -642,10 +643,13 @@ namespace euf {
e->push();
m_egraph.push();
m_relevancy.push();
m_polarities.push();
}
void solver::pop(unsigned n) {
start_reinit(n);
m_polarities.pop(n);
m_trail.pop_scope(n);
for (auto* e : m_solvers)
e->pop(n);

View file

@ -20,6 +20,7 @@ Author:
#include "util/trail.h"
#include "ast/ast_translation.h"
#include "ast/ast_util.h"
#include "ast/expr_polarities.h"
#include "ast/euf/euf_egraph.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/converters/model_converter.h"
@ -152,6 +153,7 @@ namespace euf {
svector<scope> m_scopes;
scoped_ptr_vector<th_solver> m_solvers;
ptr_vector<th_solver> m_id2solver;
expr_polarities m_polarities;
constraint* m_conflict = nullptr;
constraint* m_eq = nullptr;
@ -451,6 +453,9 @@ namespace euf {
bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override;
// internalize
void add_polarities(expr* f);
bool is_neg(expr* e) const { return m_polarities.has_negative(e); }
bool is_pos(expr* e) const { return m_polarities.has_positive(e); }
sat::literal internalize(expr* e, bool sign, bool root) override;
void internalize(expr* e) override;
sat::literal mk_literal(expr* e);

View file

@ -47,6 +47,10 @@ namespace q {
if (l.sign() == is_forall(e)) {
if (m_quantifiers_are_positive && is_forall(e))
return;
if (is_forall(e) && ctx.is_pos(e)) {
verbose_stream() << "is positive\n";
return;
}
sat::literal lit = skolemize(q);
add_clause(~l, lit);
return;

View file

@ -224,7 +224,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
return v;
}
unsigned m_num_scopes{ 0 };
unsigned m_num_scopes = 0;
void force_push() {
for (; m_num_scopes > 0; --m_num_scopes) {
@ -785,6 +785,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
};
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()
@ -840,6 +841,9 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
sat::literal internalize(expr* n) override {
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
if (ext)
ext->add_polarities(n);
bool is_not = m.is_not(n, n);
flet<bool> _top(m_top_level, false);
unsigned sz = m_result_stack.size();
@ -889,6 +893,10 @@ struct goal2sat::imp : public sat::sat_internalizer {
}
void process(expr * n) {
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
if (ext)
ext->add_polarities(n);
flet<bool> _top(m_top_level, true);
VERIFY(m_result_stack.empty());
TRACE("goal2sat", tout << "assert: " << mk_bounded_pp(n, m, 3) << "\n";);

View file

@ -258,12 +258,11 @@ namespace smt {
TRACE("arith_eq_adapter", tout << "restart\n";);
enode_pair_vector tmp(m_restart_pairs);
m_restart_pairs.reset();
for (auto const& p : tmp) {
for (auto const& [a, b] : tmp) {
if (ctx.inconsistent())
break;
TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << p.first->get_owner_id() << " #" <<
p.second->get_owner_id() << "\n";);
mk_axioms(p.first, p.second);
TRACE("arith_eq_adapter", tout << "creating arith_eq_adapter axioms at the base level #" << a->get_owner_id() << " #" << b->get_owner_id() << "\n";);
mk_axioms(a, b);
}
}

View file

@ -59,12 +59,9 @@ namespace smt {
m_relevancy_propagator(mk_relevancy_propagator(*this)),
m_user_propagator(nullptr),
m_random(p.m_random_seed),
m_flushing(false),
m_lemma_id(0),
m_progress_callback(nullptr),
m_next_progress_sample(0),
m_clause_proof(*this),
m_fingerprints(m, m_region),
m_polarities(m),
m_b_internalized_stack(m),
m_e_internalized_stack(m),
m_l_internalized_stack(m),
@ -2975,6 +2972,7 @@ namespace smt {
// logical context became inconsistent during user PUSH
VERIFY(!resolve_conflict()); // build the proof
}
m_polarities.push();
push_scope();
m_base_scopes.push_back(base_scope());
base_scope & bs = m_base_scopes.back();
@ -2988,9 +2986,11 @@ namespace smt {
void context::pop(unsigned num_scopes) {
SASSERT (num_scopes > 0);
if (num_scopes > m_scope_lvl) return;
if (num_scopes > m_scope_lvl)
return;
pop_to_base_lvl();
pop_scope(num_scopes);
m_polarities.pop(num_scopes);
}
/**

View file

@ -19,6 +19,7 @@ Revision History:
#pragma once
#include "ast/quantifier_stat.h"
#include "ast/expr_polarities.h"
#include "smt/smt_clause.h"
#include "smt/smt_setup.h"
#include "smt/smt_enode.h"
@ -92,13 +93,14 @@ namespace smt {
scoped_ptr<relevancy_propagator> m_relevancy_propagator;
theory_user_propagator* m_user_propagator;
random_gen m_random;
bool m_flushing; // (debug support) true when flushing
mutable unsigned m_lemma_id;
progress_callback * m_progress_callback;
unsigned m_next_progress_sample;
bool m_flushing = false; // (debug support) true when flushing
mutable unsigned m_lemma_id = 0;
progress_callback * m_progress_callback = nullptr;
unsigned m_next_progress_sample = 0;
clause_proof m_clause_proof;
region m_region;
fingerprint_set m_fingerprints;
expr_polarities m_polarities;
expr_ref_vector m_b_internalized_stack; // stack of the boolean expressions already internalized.
// Remark: boolean expressions can also be internalized as
@ -1550,6 +1552,7 @@ namespace smt {
// -----------------------------------
void assert_expr_core(expr * e, proof * pr);
// copy plugins into a fresh context.
void copy_plugins(context& src, context& dst);
@ -1626,6 +1629,9 @@ namespace smt {
void assert_expr(expr * e, proof * pr);
bool is_positive(expr* e) const { return m_polarities.has_positive(e); }
bool is_negative(expr* e) const { return m_polarities.has_negative(e); }
void internalize_assertions();
void push();

View file

@ -392,6 +392,8 @@ namespace smt {
if (m.is_true(n) || m.is_false(n))
return;
m_polarities.add(n);
if (m.is_not(n) && gate_ctx) {
// a boolean variable does not need to be created if n a NOT gate is in
// the context of a gate.

View file

@ -1379,6 +1379,18 @@ namespace smt {
TRACE("arith_verbose", tout << "p" << v << " := " << (is_true?"true":"false") << "\n";);
atom * a = get_bv2a(v);
if (!a) return;
#if 0
expr* f = ctx.bool_var2expr(v);
if (ctx.is_positive(f) || ctx.is_negative(f)) {
if (is_true && !ctx.is_positive(f)) {
return;
}
if (!is_true && !ctx.is_negative(f)) {
return;
}
}
#endif
SASSERT(ctx.get_assignment(a->get_bool_var()) != l_undef);
SASSERT((ctx.get_assignment(a->get_bool_var()) == l_true) == is_true);
a->assign_eh(is_true, get_epsilon(a->get_var()));
@ -1491,10 +1503,6 @@ namespace smt {
final_check_status result = FC_DONE;
final_check_status ok;
//display(verbose_stream());
//exit(0);
if (false)
{
verbose_stream() << "final\n";
@ -1514,6 +1522,7 @@ namespace smt {
switch (m_final_check_idx) {
case 0:
ok = check_int_feasibility();
if (ok != FC_DONE) verbose_stream() << "int-feas\n";
TRACE("arith", tout << "check_int_feasibility(), ok: " << ok << "\n";);
break;
case 1:
@ -1521,6 +1530,7 @@ namespace smt {
ok = FC_CONTINUE;
else
ok = FC_DONE;
if (ok != FC_DONE) verbose_stream() << "assume-eqs\n";
TRACE("arith", tout << "assume_eqs(), ok: " << ok << "\n";);
break;
default:
@ -1557,7 +1567,7 @@ namespace smt {
template<typename Ext>
final_check_status theory_arith<Ext>::final_check_eh() {
// verbose_stream() << "final " << ctx.get_scope_level() << " " << ctx.assigned_literals().size() << "\n";
verbose_stream() << "final " << ctx.get_scope_level() << " " << ctx.assigned_literals().size() << "\n";
// ctx.display(verbose_stream());
// exit(0);
@ -1566,14 +1576,14 @@ namespace smt {
if (!propagate_core())
return FC_CONTINUE;
if (delayed_assume_eqs())
if (delayed_assume_eqs()) {
verbose_stream() << "delayed-eqs\n";
return FC_CONTINUE;
}
ctx.push_trail(value_trail<unsigned>(m_final_check_idx));
m_liberal_final_check = true;
m_changed_assignment = false;
final_check_status result = final_check_core();
//display(verbose_stream());
//exit(0);
if (result != FC_DONE)
return result;
if (!m_changed_assignment)

View file

@ -1255,7 +1255,6 @@ public:
}
void internalize_eq_eh(app * atom, bool_var) {
return;
if (!ctx().get_fparams().m_arith_eager_eq_axioms)
return;
expr* lhs = nullptr, *rhs = nullptr;
@ -1265,15 +1264,26 @@ public:
if (is_arith(n1) && is_arith(n2) &&
n1->get_th_var(get_id()) != null_theory_var &&
n2->get_th_var(get_id()) != null_theory_var && n1 != n2) {
verbose_stream() << "ineq\n";
// verbose_stream() << "ineq\n";
m_arith_eq_adapter.mk_axioms(n1, n2);
}
else
verbose_stream() << "skip\n";
// else
// verbose_stream() << "skip\n";
}
void assign_eh(bool_var v, bool is_true) {
TRACE("arith", tout << mk_bounded_pp(ctx().bool_var2expr(v), m) << " " << (literal(v, !is_true)) << "\n";);
#if 0
expr* f = ctx().bool_var2expr(v);
if (ctx().is_positive(f) || ctx().is_negative(f)) {
if (is_true && !ctx().is_positive(f)) {
return;
}
if (!is_true && !ctx().is_negative(f)) {
return;
}
}
#endif
m_asserted_atoms.push_back(delayed_atom(v, is_true));
}
@ -1305,7 +1315,11 @@ public:
if (!is_int(v1) && !is_real(v1))
return;
if (true) {
if (false) {
m_deqs.push_back({v1, v2});
ctx().push_trail(push_back_vector(m_deqs));
}
else if (false) {
enode* n1 = get_enode(v1);
enode* n2 = get_enode(v2);
lpvar w1 = register_theory_var_in_lar_solver(v1);
@ -1342,6 +1356,7 @@ public:
m_arith_eq_adapter.new_diseq_eh(v1, v2);
}
// actually have to go over entire stack to ensure diseqs are respected after mutations
bool delayed_diseqs() {
if (m_diseqs_qhead == m_diseqs.size())
return false;
@ -1360,6 +1375,28 @@ public:
return has_eq;
}
svector<std::pair<theory_var, theory_var>> m_deqs;
unsigned m_eqs_qhead = 0;
// actually have to go over entire stack to ensure eqs are respected after mutations
bool delayed_eqs() {
if (m_eqs_qhead == m_deqs.size())
return false;
ctx().push_trail(value_trail(m_eqs_qhead));
bool has_eq = false;
while (m_eqs_qhead < m_deqs.size()) {
auto [v1,v2] = m_deqs[m_eqs_qhead];
if (!is_eq(v1, v2)) {
//verbose_stream() << "bad diseq " << m_diseqs_qhead << "\n";
m_arith_eq_adapter.new_eq_eh(v1, v2);
has_eq = true;
}
++m_eqs_qhead;
}
return has_eq;
}
void apply_sort_cnstr(enode* n, sort*) {
TRACE("arith", tout << "sort constraint: " << enode_pp(n, ctx()) << "\n";);
#if 0
@ -1845,7 +1882,10 @@ public:
if (delayed_diseqs())
return true;
if (delayed_eqs())
return true;
theory_var sz = static_cast<theory_var>(th.get_num_vars());
unsigned old_sz = m_assume_eq_candidates.size();
@ -1989,7 +2029,7 @@ public:
final_check_status final_check_eh() {
// verbose_stream() << "final " << ctx().get_scope_level() << " " << ctx().assigned_literals().size() << "\n";
verbose_stream() << "final " << ctx().get_scope_level() << " " << ctx().assigned_literals().size() << "\n";
//ctx().display(verbose_stream());
//exit(0);
@ -1998,14 +2038,14 @@ public:
if (propagate_core())
return FC_CONTINUE;
if (delayed_assume_eqs())
if (delayed_assume_eqs()) {
verbose_stream() << "delayed-eqs\n";
return FC_CONTINUE;
}
m_liberal_final_check = true;
m_changed_assignment = false;
ctx().push_trail(value_trail<unsigned>(m_final_check_idx));
final_check_status result = final_check_core();
//display(verbose_stream());
//exit(0);
if (result != FC_DONE)
return result;
if (!m_changed_assignment)
@ -2019,7 +2059,6 @@ public:
final_check_status final_check_core() {
if (false)
{
verbose_stream() << "final\n";
@ -2094,10 +2133,12 @@ public:
switch (m_final_check_idx) {
case 0:
st = check_lia();
if (st != FC_DONE) verbose_stream() << "check-lia\n";
break;
case 1:
if (assume_eqs())
st = FC_CONTINUE;
st = FC_CONTINUE;
if (st != FC_DONE) verbose_stream() << "assume-eqs\n";
break;
case 2:
st = check_nla();