3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-12 12:08:18 +00:00

fixes to bv/dual-solver,

This commit is contained in:
Nikolaj Bjorner 2020-11-08 17:17:43 -08:00
parent a4354c960c
commit 89ffb45c4f
19 changed files with 94 additions and 75 deletions

View file

@ -245,7 +245,7 @@ public:
bool is_unsigned(expr const * n, unsigned& u) const { bool is_unsigned(expr const * n, unsigned& u) const {
rational val; rational val;
bool is_int = true; bool is_int = true;
return is_numeral(n, val, is_int) && is_int && val.is_unsigned(), u = val.get_unsigned(), true; return is_numeral(n, val, is_int) && is_int && val.is_unsigned() && (u = val.get_unsigned(), true);
} }
bool is_numeral(expr const * n, rational & val, bool & is_int) const; bool is_numeral(expr const * n, rational & val, bool & is_int) const;
bool is_numeral(expr const * n, rational & val) const { bool is_int; return is_numeral(n, val, is_int); } bool is_numeral(expr const * n, rational & val) const { bool is_int; return is_numeral(n, val, is_int); }

View file

@ -95,7 +95,7 @@ unmerge(a,a')
- n1 is reinserted. It used to be a root. - n1 is reinserted. It used to be a root.
--*/ */
#include "ast/euf/euf_egraph.h" #include "ast/euf/euf_egraph.h"
#include "ast/ast_pp.h" #include "ast/ast_pp.h"
@ -317,11 +317,22 @@ namespace euf {
void egraph::set_merge_enabled(enode* n, bool enable_merge) { void egraph::set_merge_enabled(enode* n, bool enable_merge) {
if (enable_merge != n->merge_enabled()) { if (enable_merge != n->merge_enabled()) {
toggle_merge_enabled(n);
m_updates.push_back(update_record(n, update_record::toggle_merge())); m_updates.push_back(update_record(n, update_record::toggle_merge()));
n->set_merge_enabled(enable_merge);
} }
} }
void egraph::toggle_merge_enabled(enode* n) {
bool enable_merge = !n->merge_enabled();
n->set_merge_enabled(enable_merge);
if (n->num_args() > 0) {
if (enable_merge)
insert_table(n);
else
m_table.erase(n);
}
}
void egraph::set_value(enode* n, lbool value) { void egraph::set_value(enode* n, lbool value) {
force_push(); force_push();
TRACE("euf", tout << bpp(n) << "\n";); TRACE("euf", tout << bpp(n) << "\n";);
@ -362,7 +373,7 @@ namespace euf {
undo_node(); undo_node();
break; break;
case update_record::tag_t::is_toggle_merge: case update_record::tag_t::is_toggle_merge:
p.r1->set_merge_enabled(!p.r1->merge_enabled()); toggle_merge_enabled(p.r1);
break; break;
case update_record::tag_t::is_set_parent: case update_record::tag_t::is_set_parent:
undo_eq(p.r1, p.n1, p.r2_num_parents); undo_eq(p.r1, p.n1, p.r2_num_parents);
@ -520,6 +531,12 @@ namespace euf {
for (enode* c : enode_class(r1)) for (enode* c : enode_class(r1))
c->m_root = r1; c->m_root = r1;
for (enode* p : enode_parents(r1))
if (p->merge_enabled() && !p->is_cgr() && !p->m_cg) {
std::cout << bpp(p) << "\n";
SASSERT(false);
}
for (enode* p : enode_parents(r1)) for (enode* p : enode_parents(r1))
if (p->merge_enabled() && (p->is_cgr() || !p->congruent(p->m_cg))) if (p->merge_enabled() && (p->is_cgr() || !p->congruent(p->m_cg)))
insert_table(p); insert_table(p);

View file

@ -196,6 +196,7 @@ namespace euf {
void push_to_lca(enode* a, enode* lca); void push_to_lca(enode* a, enode* lca);
void push_congruence(enode* n1, enode* n2, bool commutative); void push_congruence(enode* n1, enode* n2, bool commutative);
void push_todo(enode* n); void push_todo(enode* n);
void toggle_merge_enabled(enode* n);
enode_bool_pair insert_table(enode* p); enode_bool_pair insert_table(enode* p);

View file

@ -138,7 +138,7 @@ namespace euf {
bool is_cgr() const { return this == m_cg; } bool is_cgr() const { return this == m_cg; }
bool commutative() const { return m_commutative; } bool commutative() const { return m_commutative; }
void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; } void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; }
bool merge_enabled() { return m_merge_enabled; } bool merge_enabled() const { return m_merge_enabled; }
enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; } enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }
unsigned hash() const { return m_expr->hash(); } unsigned hash() const { return m_expr->hash(); }

View file

@ -1192,10 +1192,10 @@ namespace lp {
impq lar_solver::get_tv_ivalue(tv const& t) const { impq lar_solver::get_tv_ivalue(tv const& t) const {
if (t.is_var()) if (t.is_var())
return get_ivalue(t.column()); return get_column_value(t.column());
impq r; impq r;
for (const auto& p : get_term(t)) for (const auto& p : get_term(t))
r += p.coeff() * get_ivalue(p.column()); r += p.coeff() * get_column_value(p.column());
return r; return r;
} }
@ -1317,9 +1317,8 @@ namespace lp {
void lar_solver::mark_rows_for_bound_prop(lpvar j) { void lar_solver::mark_rows_for_bound_prop(lpvar j) {
auto& column = A_r().m_columns[j]; auto& column = A_r().m_columns[j];
for (auto const& r : column) { for (auto const& r : column)
m_rows_with_changed_bounds.insert(r.var()); m_rows_with_changed_bounds.insert(r.var());
}
} }

View file

@ -638,7 +638,7 @@ public:
inline const static_matrix<mpq, impq> & A_r() const { return m_mpq_lar_core_solver.m_r_A; } inline const static_matrix<mpq, impq> & A_r() const { return m_mpq_lar_core_solver.m_r_A; }
// columns // columns
bool column_is_int(column_index const& j) const { return column_is_int((unsigned)j); } bool column_is_int(column_index const& j) const { return column_is_int((unsigned)j); }
const impq& get_ivalue(column_index const& j) const { return get_column_value(j); } // const impq& get_ivalue(column_index const& j) const { return get_column_value(j); }
const impq& get_column_value(column_index const& j) const { return m_mpq_lar_core_solver.m_r_x[j]; } const impq& get_column_value(column_index const& j) const { return m_mpq_lar_core_solver.m_r_x[j]; }
inline inline
var_index external_to_local(unsigned j) const { var_index external_to_local(unsigned j) const {

View file

@ -51,7 +51,6 @@ namespace arith {
solver::~solver() { solver::~solver() {
del_bounds(0); del_bounds(0);
std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc<internalize_state>());
} }
void solver::asserted(literal l) { void solver::asserted(literal l) {

View file

@ -111,7 +111,7 @@ namespace arith {
m_to_ensure_var.reset(); m_to_ensure_var.reset();
} }
}; };
ptr_vector<internalize_state> m_internalize_states; scoped_ptr_vector<internalize_state> m_internalize_states;
unsigned m_internalize_head{ 0 }; unsigned m_internalize_head{ 0 };
class scoped_internalize_state { class scoped_internalize_state {

View file

@ -223,6 +223,9 @@ namespace array {
args1.push_back(k); args1.push_back(k);
args2.push_back(k); args2.push_back(k);
} }
std::cout << "e1: " << mk_pp(e1, m) << "\n";
std::cout << "e2: " << mk_pp(e2, m) << "\n";
std::cout << "funcs: " << funcs << "\n";
expr_ref sel1(a.mk_select(args1), m); expr_ref sel1(a.mk_select(args1), m);
expr_ref sel2(a.mk_select(args2), m); expr_ref sel2(a.mk_select(args2), m);
literal lit1 = eq_internalize(e1, e2); literal lit1 = eq_internalize(e1, e2);
@ -521,13 +524,11 @@ namespace array {
for (unsigned i = 0; i < num_vars; i++) { for (unsigned i = 0; i < num_vars; i++) {
euf::enode * n = var2enode(i); euf::enode * n = var2enode(i);
if (!a.is_array(n->get_expr())) { if (!is_array(n))
continue; continue;
}
euf::enode * r = n->get_root(); euf::enode * r = n->get_root();
if (r->is_marked1()) { if (r->is_marked1())
continue; continue;
}
// arrays used as indices in other arrays have to be treated as shared issue #3532, #3529 // arrays used as indices in other arrays have to be treated as shared issue #3532, #3529
if (ctx.is_shared(r) || is_select_arg(r)) if (ctx.is_shared(r) || is_select_arg(r))
roots.push_back(r->get_th_var(get_id())); roots.push_back(r->get_th_var(get_id()));

View file

@ -86,6 +86,7 @@ namespace array {
} }
void solver::internalize_ext(euf::enode* n) { void solver::internalize_ext(euf::enode* n) {
SASSERT(is_array(n->get_arg(0)));
push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1))); push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1)));
} }

View file

@ -165,7 +165,10 @@ namespace array {
void solver::new_diseq_eh(euf::th_eq const& eq) { void solver::new_diseq_eh(euf::th_eq const& eq) {
force_push(); force_push();
push_axiom(extensionality_axiom(var2enode(eq.v1()), var2enode(eq.v2()))); euf::enode* n1 = var2enode(eq.v1());
euf::enode* n2 = var2enode(eq.v2());
if (is_array(n1))
push_axiom(extensionality_axiom(n1, n2));
} }
bool solver::unit_propagate() { bool solver::unit_propagate() {

View file

@ -158,6 +158,7 @@ namespace array {
void collect_shared_vars(sbuffer<theory_var>& roots); void collect_shared_vars(sbuffer<theory_var>& roots);
bool add_interface_equalities(); bool add_interface_equalities();
bool is_select_arg(euf::enode* r); bool is_select_arg(euf::enode* r);
bool is_array(euf::enode* n) const { return a.is_array(n->get_expr()); }
// solving // solving
void add_parent_select(theory_var v_child, euf::enode* select); void add_parent_select(theory_var v_child, euf::enode* select);

View file

@ -176,10 +176,10 @@ namespace bv {
args[i] = arg_value; args[i] = arg_value;
expr_ref r(m.mk_app(n->get_decl(), args), m); expr_ref r(m.mk_app(n->get_decl(), args), m);
set_delay_internalize(r, internalize_mode::init_bits_only_i); // do not bit-blast this multiplier. set_delay_internalize(r, internalize_mode::init_bits_only_i); // do not bit-blast this multiplier.
args[i] = n->get_arg(i); args[i] = n->get_arg(i);
std::cout << "@" << s().scope_lvl() << "\n";
add_unit(eq_internalize(r, arg_value)); add_unit(eq_internalize(r, arg_value));
} }
IF_VERBOSE(0, verbose_stream() << "delay internalize @" << s().scope_lvl() << "\n");
return false; return false;
} }
if (bv.is_zero(mul_value)) { if (bv.is_zero(mul_value)) {

View file

@ -133,6 +133,9 @@ namespace bv {
euf::enode* n = expr2enode(e); euf::enode* n = expr2enode(e);
app* a = to_app(e); app* a = to_app(e);
if (visited(e))
return true;
SASSERT(!n || !n->is_attached_to(get_id())); SASSERT(!n || !n->is_attached_to(get_id()));
bool suppress_args = !get_config().m_bv_reflect && !m.is_considered_uninterpreted(a->get_decl()); bool suppress_args = !get_config().m_bv_reflect && !m.is_considered_uninterpreted(a->get_decl());
if (!n) if (!n)
@ -294,27 +297,12 @@ namespace bv {
atom* a = get_bv2a(bv); atom* a = get_bv2a(bv);
if (a) if (a)
return a; return a;
a = new (get_region()) atom(); a = new (get_region()) atom(bv);
insert_bv2a(bv, a); insert_bv2a(bv, a);
ctx.push(mk_atom_trail(bv, *this)); ctx.push(mk_atom_trail(bv, *this));
return a; return a;
} }
#if 0
solver::eq_atom* solver::mk_eq_atom(sat::bool_var bv) {
atom* a = get_bv2a(bv);
if (a)
return a->is_eq() ? &a->to_eq() : nullptr;
else {
eq_atom* b = new (get_region()) eq_atom();
insert_bv2a(bv, b);
ctx.push(mk_atom_trail(bv, *this));
return b;
}
}
#endif
void solver::set_bit_eh(theory_var v, literal l, unsigned idx) { void solver::set_bit_eh(theory_var v, literal l, unsigned idx) {
SASSERT(m_bits[v][idx] == l); SASSERT(m_bits[v][idx] == l);
if (s().value(l) != l_undef && s().lvl(l) == 0) if (s().value(l) != l_undef && s().lvl(l) == 0)
@ -323,8 +311,7 @@ namespace bv {
atom* b = mk_atom(l.var()); atom* b = mk_atom(l.var());
if (b->m_occs) if (b->m_occs)
find_new_diseq_axioms(*b, v, idx); find_new_diseq_axioms(*b, v, idx);
if (!b->is_fresh()) ctx.push(add_var_pos_trail(b));
ctx.push(add_var_pos_trail(b));
b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs); b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs);
} }
} }
@ -586,7 +573,7 @@ namespace bv {
} }
void solver::add_def(sat::literal def, sat::literal l) { void solver::add_def(sat::literal def, sat::literal l) {
atom* a = new (get_region()) atom(); atom* a = new (get_region()) atom(l.var());
a->m_var = l; a->m_var = l;
a->m_def = def; a->m_def = def;
insert_bv2a(l.var(), a); insert_bv2a(l.var(), a);
@ -648,7 +635,7 @@ namespace bv {
m_bits[v_arg][idx] = lit; m_bits[v_arg][idx] = lit;
TRACE("bv", tout << "add-bit: " << lit << " " << literal2expr(lit) << "\n";); TRACE("bv", tout << "add-bit: " << lit << " " << literal2expr(lit) << "\n";);
if (arg_sz > 1) { if (arg_sz > 1) {
atom* a = new (get_region()) atom(); atom* a = new (get_region()) atom(lit.var());
a->m_occs = new (get_region()) var_pos_occ(v_arg, idx); a->m_occs = new (get_region()) var_pos_occ(v_arg, idx);
insert_bv2a(lit.var(), a); insert_bv2a(lit.var(), a);
ctx.push(mk_atom_trail(lit.var(), *this)); ctx.push(mk_atom_trail(lit.var(), *this));
@ -713,20 +700,12 @@ namespace bv {
void solver::eq_internalized(sat::bool_var b1, sat::bool_var b2, unsigned idx, theory_var v1, theory_var v2, literal lit, euf::enode* n) { void solver::eq_internalized(sat::bool_var b1, sat::bool_var b2, unsigned idx, theory_var v1, theory_var v2, literal lit, euf::enode* n) {
atom* a = mk_atom(b1); atom* a = mk_atom(b1);
// eq_atom* b = mk_eq_atom(lit.var());
if (a) { if (a) {
if (!a->is_fresh()) ctx.push(add_eq_occurs_trail(a));
ctx.push(add_eq_occurs_trail(a));
auto* next = a->m_eqs; auto* next = a->m_eqs;
a->m_eqs = new (get_region()) eq_occurs(b1, b2, idx, v1, v2, lit, n, next); a->m_eqs = new (get_region()) eq_occurs(b1, b2, idx, v1, v2, lit, n, next);
if (next) if (next)
next->m_prev = a->m_eqs; next->m_prev = a->m_eqs;
#if 0
if (b) {
b->m_eqs.push_back(std::make_pair(a, a->m_eqs));
ctx.push(push_back_vector<euf::solver, svector<std::pair<bit_atom*,eq_occurs*>>>(b->m_eqs));
}
#endif
} }
} }

View file

@ -44,7 +44,6 @@ namespace bv {
bit_occs_trail(solver& s, atom& a): a(a), m_occs(a.m_occs) {} bit_occs_trail(solver& s, atom& a): a(a), m_occs(a.m_occs) {}
virtual void undo(euf::solver& euf) { virtual void undo(euf::solver& euf) {
IF_VERBOSE(1, verbose_stream() << "add back occurrences " << & a << "\n");
a.m_occs = m_occs; a.m_occs = m_occs;
} }
}; };
@ -400,20 +399,10 @@ namespace bv {
for (; m_prop_queue_head < m_prop_queue.size() && !s().inconsistent(); ++m_prop_queue_head) { for (; m_prop_queue_head < m_prop_queue.size() && !s().inconsistent(); ++m_prop_queue_head) {
auto const p = m_prop_queue[m_prop_queue_head]; auto const p = m_prop_queue[m_prop_queue_head];
if (p.m_atom) { if (p.m_atom) {
unsigned num_atoms = 0, num_eqs = 0, num_assigned = 0, num_eq_assigned = 0, num_lit_assigned = 0; for (auto vp : *p.m_atom)
for (auto vp : *p.m_atom) { propagate_bits(vp);
if (propagate_bits(vp)) for (auto const& eq : p.m_atom->eqs())
++num_assigned; propagate_eq_occurs(eq);
++num_atoms;
}
for (auto const& eq : p.m_atom->eqs()) {
++num_eqs;
if (s().value(eq.m_literal) != l_undef)
++num_lit_assigned;
if (propagate_eq_occurs(eq)) {
++num_eq_assigned;
}
}
} }
else else
propagate_bits(p.m_vp); propagate_bits(p.m_vp);
@ -669,7 +658,7 @@ namespace bv {
if (!a) if (!a)
continue; continue;
atom* new_a = new (result->get_region()) atom(); atom* new_a = new (result->get_region()) atom(i);
m_bool_var2atom.setx(i, new_a, nullptr); m_bool_var2atom.setx(i, new_a, nullptr);
for (auto vp : *a) for (auto vp : *a)
new_a->m_occs = new (result->get_region()) var_pos_occ(vp.first, vp.second, new_a->m_occs); new_a->m_occs = new (result->get_region()) var_pos_occ(vp.first, vp.second, new_a->m_occs);
@ -800,6 +789,7 @@ namespace bv {
find_wpos(v2); find_wpos(v2);
bool_var cv = consequent.var(); bool_var cv = consequent.var();
atom* a = get_bv2a(cv); atom* a = get_bv2a(cv);
force_push();
if (a) if (a)
for (auto curr : *a) for (auto curr : *a)
if (propagate_eqc || find(curr.first) != find(v2) || curr.second != idx) if (propagate_eqc || find(curr.first) != find(v2) || curr.second != idx)

View file

@ -126,7 +126,7 @@ namespace bv {
eq_occurs_it(eq_occurs* c) : m_first(c) {} eq_occurs_it(eq_occurs* c) : m_first(c) {}
eq_occurs operator*() { return *m_first; } eq_occurs operator*() { return *m_first; }
eq_occurs_it& operator++() { m_first = m_first->m_next; return *this; } eq_occurs_it& operator++() { m_first = m_first->m_next; return *this; }
eq_occurs_it operator++(int) { eq_occurs_it tmp = *this; ++* this; return tmp; } eq_occurs_it operator++(int) { eq_occurs_it tmp = *this; ++*this; return tmp; }
bool operator==(eq_occurs_it const& other) const { return m_first == other.m_first; } bool operator==(eq_occurs_it const& other) const { return m_first == other.m_first; }
bool operator!=(eq_occurs_it const& other) const { return !(*this == other); } bool operator!=(eq_occurs_it const& other) const { return !(*this == other); }
}; };
@ -157,16 +157,16 @@ namespace bv {
}; };
struct atom { struct atom {
bool_var m_bv;
eq_occurs* m_eqs; eq_occurs* m_eqs;
var_pos_occ * m_occs; var_pos_occ * m_occs;
svector<std::pair<atom*, eq_occurs*>> m_bit2occ; svector<std::pair<atom*, eq_occurs*>> m_bit2occ;
literal m_var { sat::null_literal }; literal m_var { sat::null_literal };
literal m_def { sat::null_literal }; literal m_def { sat::null_literal };
atom() :m_eqs(nullptr), m_occs(nullptr) {} atom(bool_var b) :m_bv(b), m_eqs(nullptr), m_occs(nullptr) {}
~atom() { m_bit2occ.clear(); } ~atom() { m_bit2occ.clear(); }
var_pos_it begin() const { return var_pos_it(m_occs); } var_pos_it begin() const { return var_pos_it(m_occs); }
var_pos_it end() const { return var_pos_it(nullptr); } var_pos_it end() const { return var_pos_it(nullptr); }
bool is_fresh() const { return !m_occs && !m_eqs; }
eqs_iterator eqs() const { return eqs_iterator(m_eqs); } eqs_iterator eqs() const { return eqs_iterator(m_eqs); }
}; };

View file

@ -104,7 +104,7 @@ namespace euf {
sat::literal lit; sat::literal lit;
if (!m.is_bool(e)) if (!m.is_bool(e))
drat_log_node(e); drat_log_node(e);
else else
lit = attach_lit(literal(si.add_bool_var(e), false), e); lit = attach_lit(literal(si.add_bool_var(e), false), e);
if (!m.is_bool(e) && m.get_sort(e)->get_family_id() != null_family_id) { if (!m.is_bool(e) && m.get_sort(e)->get_family_id() != null_family_id) {
@ -153,6 +153,8 @@ namespace euf {
m_egraph.set_bool_var(n, 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)) if (m.is_eq(e) || m.is_or(e) || m.is_and(e) || m.is_not(e))
m_egraph.set_merge_enabled(n, false); m_egraph.set_merge_enabled(n, false);
if (!si.is_bool_op(e))
track_relevancy(lit.var());
return lit; return lit;
} }

View file

@ -90,19 +90,42 @@ namespace sat {
} }
bool dual_solver::operator()(solver const& s) { bool dual_solver::operator()(solver const& s) {
m_core.reset();
m_core.append(m_units);
if (m_roots.empty())
return true;
m_solver.user_push(); m_solver.user_push();
m_solver.add_clause(m_roots.size(), m_roots.c_ptr(), status::input()); m_solver.add_clause(m_roots.size(), m_roots.c_ptr(), status::input());
m_lits.reset(); m_lits.reset();
for (bool_var v : m_tracked_vars) for (bool_var v : m_tracked_vars)
m_lits.push_back(literal(v, l_false == s.value(m_var2ext[v]))); m_lits.push_back(literal(v, l_false == s.value(m_var2ext[v])));
lbool is_sat = m_solver.check(m_lits.size(), m_lits.c_ptr()); lbool is_sat = m_solver.check(m_lits.size(), m_lits.c_ptr());
m_core.reset();
m_core.append(m_units);
if (is_sat == l_false) if (is_sat == l_false)
for (literal lit : m_solver.get_core()) for (literal lit : m_solver.get_core())
m_core.push_back(lit2ext(lit)); m_core.push_back(lit2ext(lit));
TRACE("euf", m_solver.display(tout << "is-sat: " << is_sat << " core: " << m_core << "\n");); if (is_sat == l_true) {
IF_VERBOSE(0, display(s, verbose_stream()));
UNREACHABLE();
}
TRACE("dual", m_solver.display(tout << "is-sat: " << is_sat << " core: " << m_core << "\n"););
m_solver.user_pop(1); m_solver.user_pop(1);
return is_sat == l_false; return is_sat == l_false;
} }
std::ostream& dual_solver::display(solver const& s, std::ostream& out) const {
for (auto r : m_roots)
out << r << " " << m_solver.value(r) << "\n";
for (unsigned v = 0; v < m_solver.num_vars(); ++v) {
bool_var w = m_var2ext.get(v, null_bool_var);
if (w == null_bool_var)
continue;
lbool v1 = m_solver.value(v);
lbool v2 = s.value(w);
if (v1 == v2)
continue;
out << w << " " << v1 << " " << v2 << "\n";
}
return out;
}
} }

View file

@ -40,6 +40,9 @@ namespace sat {
bool_var var2ext(bool_var v); bool_var var2ext(bool_var v);
literal ext2lit(literal lit); literal ext2lit(literal lit);
literal lit2ext(literal lit); literal lit2ext(literal lit);
std::ostream& display(solver const& s, std::ostream& out) const;
public: public:
dual_solver(reslimit& l); dual_solver(reslimit& l);
void push(); void push();