mirror of
https://github.com/Z3Prover/z3
synced 2025-04-15 13:28:47 +00:00
adding arith sls
This commit is contained in:
parent
6a2d60a6ba
commit
96d815b904
|
@ -200,16 +200,14 @@ namespace sat {
|
||||||
m_shifts = 0;
|
m_shifts = 0;
|
||||||
m_stopwatch.start();
|
m_stopwatch.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ddfw::reinit(solver& s) {
|
void ddfw::reinit(solver& s, bool_vector const& phase) {
|
||||||
add(s);
|
add(s);
|
||||||
add_assumptions();
|
add_assumptions();
|
||||||
if (s.m_best_phase_size > 0) {
|
for (unsigned v = 0; v < phase.size(); ++v) {
|
||||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
value(v) = phase[v];
|
||||||
value(v) = s.m_best_phase[v];
|
reward(v) = 0;
|
||||||
reward(v) = 0;
|
make_count(v) = 0;
|
||||||
make_count(v) = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
init_clause_data();
|
init_clause_data();
|
||||||
flatten_use_list();
|
flatten_use_list();
|
||||||
|
|
|
@ -227,7 +227,7 @@ namespace sat {
|
||||||
|
|
||||||
// for parallel integration
|
// for parallel integration
|
||||||
unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; }
|
unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; }
|
||||||
void reinit(solver& s) override;
|
void reinit(solver& s, bool_vector const& phase) override;
|
||||||
|
|
||||||
void collect_statistics(statistics& st) const override {}
|
void collect_statistics(statistics& st) const override {}
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,7 @@ namespace sat {
|
||||||
virtual void add_assumptions(literal_set& ext_assumptions) {}
|
virtual void add_assumptions(literal_set& ext_assumptions) {}
|
||||||
virtual bool tracking_assumptions() { return false; }
|
virtual bool tracking_assumptions() { return false; }
|
||||||
virtual bool enable_self_propagate() const { return false; }
|
virtual bool enable_self_propagate() const { return false; }
|
||||||
|
virtual void local_search(bool_vector& phase) {}
|
||||||
|
|
||||||
virtual bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
|
virtual bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
|
||||||
std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) {
|
std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) {
|
||||||
|
|
|
@ -359,13 +359,10 @@ namespace sat {
|
||||||
m_par(nullptr) {
|
m_par(nullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_search::reinit(solver& s) {
|
void local_search::reinit(solver& s, bool_vector const& phase) {
|
||||||
import(s, true);
|
import(s, true);
|
||||||
if (s.m_best_phase_size > 0) {
|
for (unsigned i = phase.size(); i-- > 0; )
|
||||||
for (unsigned i = num_vars(); i-- > 0; ) {
|
set_phase(i, phase[i]);
|
||||||
set_phase(i, s.m_best_phase[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void local_search::import(solver const& s, bool _init) {
|
void local_search::import(solver const& s, bool _init) {
|
||||||
|
|
|
@ -248,7 +248,7 @@ namespace sat {
|
||||||
|
|
||||||
void set_seed(unsigned n) override { config().set_random_seed(n); }
|
void set_seed(unsigned n) override { config().set_random_seed(n); }
|
||||||
|
|
||||||
void reinit(solver& s) override;
|
void reinit(solver& s, bool_vector const& phase) override;
|
||||||
|
|
||||||
// used by unit-walk
|
// used by unit-walk
|
||||||
void set_phase(bool_var v, bool f);
|
void set_phase(bool_var v, bool f);
|
||||||
|
|
|
@ -252,7 +252,7 @@ namespace sat {
|
||||||
m_consumer_ready = true;
|
m_consumer_ready = true;
|
||||||
if (m_solver_copy) {
|
if (m_solver_copy) {
|
||||||
copied = true;
|
copied = true;
|
||||||
s.reinit(*m_solver_copy.get());
|
s.reinit(*m_solver_copy.get(), m_solver_copy->m_best_phase);
|
||||||
}
|
}
|
||||||
return copied;
|
return copied;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,7 @@ namespace sat {
|
||||||
|
|
||||||
void collect_statistics(statistics& st) const override {}
|
void collect_statistics(statistics& st) const override {}
|
||||||
|
|
||||||
void reinit(solver& s) override { UNREACHABLE(); }
|
void reinit(solver& s, bool_vector const& phase) override { UNREACHABLE(); }
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1373,7 +1373,7 @@ namespace sat {
|
||||||
m_backoffs.m_local_search.delta_effort(*this);
|
m_backoffs.m_local_search.delta_effort(*this);
|
||||||
m_local_search->rlimit().push(m_backoffs.m_local_search.limit);
|
m_local_search->rlimit().push(m_backoffs.m_local_search.limit);
|
||||||
|
|
||||||
m_local_search->reinit(*this);
|
m_local_search->reinit(*this, m_best_phase);
|
||||||
lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr);
|
lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr);
|
||||||
auto const& mdl = m_local_search->get_model();
|
auto const& mdl = m_local_search->get_model();
|
||||||
if (mdl.size() == m_best_phase.size()) {
|
if (mdl.size() == m_best_phase.size()) {
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace sat {
|
||||||
virtual void updt_params(params_ref const& p) = 0;
|
virtual void updt_params(params_ref const& p) = 0;
|
||||||
virtual void set_seed(unsigned s) = 0;
|
virtual void set_seed(unsigned s) = 0;
|
||||||
virtual lbool check(unsigned sz, literal const* assumptions, parallel* par) = 0;
|
virtual lbool check(unsigned sz, literal const* assumptions, parallel* par) = 0;
|
||||||
virtual void reinit(solver& s) = 0;
|
virtual void reinit(solver& s, bool_vector const& phase) = 0;
|
||||||
virtual unsigned num_non_binary_clauses() const = 0;
|
virtual unsigned num_non_binary_clauses() const = 0;
|
||||||
virtual reslimit& rlimit() = 0;
|
virtual reslimit& rlimit() = 0;
|
||||||
virtual model const& get_model() const = 0;
|
virtual model const& get_model() const = 0;
|
||||||
|
|
|
@ -3,6 +3,7 @@ z3_add_component(sat_smt
|
||||||
arith_axioms.cpp
|
arith_axioms.cpp
|
||||||
arith_diagnostics.cpp
|
arith_diagnostics.cpp
|
||||||
arith_internalize.cpp
|
arith_internalize.cpp
|
||||||
|
arith_local_search.cpp
|
||||||
arith_solver.cpp
|
arith_solver.cpp
|
||||||
array_axioms.cpp
|
array_axioms.cpp
|
||||||
array_diagnostics.cpp
|
array_diagnostics.cpp
|
||||||
|
@ -20,6 +21,7 @@ z3_add_component(sat_smt
|
||||||
euf_ackerman.cpp
|
euf_ackerman.cpp
|
||||||
euf_internalize.cpp
|
euf_internalize.cpp
|
||||||
euf_invariant.cpp
|
euf_invariant.cpp
|
||||||
|
euf_local_search.cpp
|
||||||
euf_model.cpp
|
euf_model.cpp
|
||||||
euf_proof.cpp
|
euf_proof.cpp
|
||||||
euf_proof_checker.cpp
|
euf_proof_checker.cpp
|
||||||
|
|
|
@ -23,6 +23,17 @@ Author:
|
||||||
|
|
||||||
namespace arith {
|
namespace arith {
|
||||||
|
|
||||||
|
|
||||||
|
void arith_proof_hint_builder::set_type(euf::solver& ctx, hint_type ty) {
|
||||||
|
ctx.push(value_trail<unsigned>(m_eq_tail));
|
||||||
|
ctx.push(value_trail<unsigned>(m_lit_tail));
|
||||||
|
m_ty = ty;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
arith_proof_hint* arith_proof_hint_builder::mk(euf::solver& s) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& solver::display(std::ostream& out) const {
|
std::ostream& solver::display(std::ostream& out) const {
|
||||||
lp().display(out);
|
lp().display(out);
|
||||||
|
|
352
src/sat/smt/arith_local_search.cpp
Normal file
352
src/sat/smt/arith_local_search.cpp
Normal file
|
@ -0,0 +1,352 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2020 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
arith_local_search.cpp
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Local search dispatch for SMT
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2023-02-07
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#include "sat/sat_solver.h"
|
||||||
|
#include "sat/smt/arith_solver.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace arith {
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
/// need access to clauses
|
||||||
|
/// need access to m_unsat
|
||||||
|
/// need update of phase
|
||||||
|
/// need to initialize ineqs (arithmetical atoms)
|
||||||
|
///
|
||||||
|
|
||||||
|
solver::sls::sls(solver& s):
|
||||||
|
s(s), m(s.m) {}
|
||||||
|
|
||||||
|
void solver::sls::operator()(bool_vector& phase) {
|
||||||
|
|
||||||
|
// need to init variables/atoms/ineqs
|
||||||
|
|
||||||
|
m.limit().push(m_max_arith_steps);
|
||||||
|
|
||||||
|
unsigned m_best_min_unsat = 1;
|
||||||
|
unsigned best = m_best_min_unsat;
|
||||||
|
|
||||||
|
while (m.inc() && m_best_min_unsat > 0) {
|
||||||
|
// unsigned prev = m_unsat.size();
|
||||||
|
if (!flip())
|
||||||
|
return;
|
||||||
|
#if 0
|
||||||
|
if (m_unsat.size() < best) {
|
||||||
|
best = m_unsat.size();
|
||||||
|
num_steps = 0;
|
||||||
|
}
|
||||||
|
if (m_unsat.size() < m_best_min_unsat)
|
||||||
|
save_best_values();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::sls::set_bounds_begin() {
|
||||||
|
m_max_arith_steps = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::sls::set_bounds_end(unsigned num_literals) {
|
||||||
|
// m_max_arith_steps = s.ctx.m_sl_config.L *
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::sls::set_bounds(enode* n) {
|
||||||
|
++m_max_arith_steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool solver::sls::flip() {
|
||||||
|
++m_stats.m_num_flips;
|
||||||
|
log();
|
||||||
|
if (flip_unsat())
|
||||||
|
return true;
|
||||||
|
if (flip_clauses())
|
||||||
|
return true;
|
||||||
|
if (flip_dscore())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// distance to true
|
||||||
|
rational solver::sls::dtt(rational const& args, ineq const& ineq) const {
|
||||||
|
switch (ineq.m_op) {
|
||||||
|
case ineq_kind::LE:
|
||||||
|
if (args <= ineq.m_bound)
|
||||||
|
return rational::zero();
|
||||||
|
return args - ineq.m_bound;
|
||||||
|
case ineq_kind::EQ:
|
||||||
|
if (args == ineq.m_bound)
|
||||||
|
return rational::zero();
|
||||||
|
return rational::one();
|
||||||
|
case ineq_kind::NE:
|
||||||
|
if (args == ineq.m_bound)
|
||||||
|
return rational::one();
|
||||||
|
return rational::zero();
|
||||||
|
case ineq_kind::LT:
|
||||||
|
default:
|
||||||
|
if (args < ineq.m_bound)
|
||||||
|
return rational::zero();
|
||||||
|
return args - ineq.m_bound + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rational solver::sls::dtt(ineq const& ineq, var_t v, rational const& new_value) const {
|
||||||
|
auto new_args_value = ineq.m_args_value;
|
||||||
|
for (auto const& [coeff, w] : ineq.m_args) {
|
||||||
|
if (w == v) {
|
||||||
|
new_args_value += coeff * (new_value - m_vars[w].m_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dtt(new_args_value, ineq);
|
||||||
|
}
|
||||||
|
|
||||||
|
// critical move
|
||||||
|
bool solver::sls::cm(ineq const& ineq, var_t v, rational& new_value) {
|
||||||
|
SASSERT(!ineq.is_true());
|
||||||
|
auto delta = ineq.m_args_value - ineq.m_bound;
|
||||||
|
for (auto const& [coeff, w] : ineq.m_args) {
|
||||||
|
if (w == v) {
|
||||||
|
if (coeff > 0)
|
||||||
|
new_value = value(v) - abs(ceil(delta / coeff));
|
||||||
|
else
|
||||||
|
new_value = value(v) + abs(floor(delta / coeff));
|
||||||
|
switch (ineq.m_op) {
|
||||||
|
case ineq_kind::LE:
|
||||||
|
SASSERT(delta + coeff * (new_value - value(v)) <= 0);
|
||||||
|
return true;
|
||||||
|
case ineq_kind::EQ:
|
||||||
|
return delta + coeff * (new_value - value(v)) == 0;
|
||||||
|
case ineq_kind::NE:
|
||||||
|
return delta + coeff * (new_value - value(v)) != 0;
|
||||||
|
case ineq_kind::LT:
|
||||||
|
return delta + coeff * (new_value - value(v)) < 0;
|
||||||
|
default:
|
||||||
|
UNREACHABLE(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
bool solver::sls::flip_unsat() {
|
||||||
|
unsigned start = m_rand();
|
||||||
|
for (unsigned i = m_unsat.size(); i-- > 0; ) {
|
||||||
|
unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size());
|
||||||
|
if (flip(m_clauses[cl]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool solver::sls::flip_clauses() {
|
||||||
|
unsigned start = m_rand();
|
||||||
|
for (unsigned i = m_clauses.size(); i-- > 0; )
|
||||||
|
if (flip_arith(m_clauses[(i + start) % m_clauses.size()]))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool solver::sls::flip_dscore() {
|
||||||
|
paws();
|
||||||
|
unsigned start = m_rand();
|
||||||
|
for (unsigned i = m_unsat.size(); i-- > 0; ) {
|
||||||
|
unsigned cl = m_unsat.elem_at((i + start) % m_unsat.size());
|
||||||
|
if (flip_dscore(m_clauses[cl]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
std::cout << "flip dscore\n";
|
||||||
|
IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << m_unsat.size() << ")\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool solver::sls::flip_dscore(clause const& clause) {
|
||||||
|
rational new_value, min_value, min_score(-1);
|
||||||
|
var_t min_var = UINT_MAX;
|
||||||
|
for (auto a : clause.m_arith) {
|
||||||
|
auto const& ai = m_atoms[a];
|
||||||
|
ineq const& ineq = ai.m_ineq;
|
||||||
|
for (auto const& [coeff, v] : ineq.m_args) {
|
||||||
|
if (!ineq.is_true() && cm(ineq, v, new_value)) {
|
||||||
|
rational score = dscore(v, new_value);
|
||||||
|
if (UINT_MAX == min_var || score < min_score) {
|
||||||
|
min_var = v;
|
||||||
|
min_value = new_value;
|
||||||
|
min_score = score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min_var != UINT_MAX) {
|
||||||
|
update(min_var, min_value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::sls::paws() {
|
||||||
|
for (auto& clause : m_clauses) {
|
||||||
|
bool above = 10000 * m_config.sp <= (m_rand() % 10000);
|
||||||
|
if (!above && clause.is_true() && clause.m_weight > 1)
|
||||||
|
clause.m_weight -= 1;
|
||||||
|
if (above && !clause.is_true())
|
||||||
|
clause.m_weight += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::sls::update(var_t v, rational const& new_value) {
|
||||||
|
auto& vi = m_vars[v];
|
||||||
|
auto const& old_value = vi.m_value;
|
||||||
|
for (auto const& [coeff, atm] : vi.m_atoms) {
|
||||||
|
auto& ai = m_atoms[atm];
|
||||||
|
SASSERT(!ai.m_is_bool);
|
||||||
|
auto& clause = m_clauses[ai.m_clause_idx];
|
||||||
|
rational dtt_old = dtt(ai.m_ineq);
|
||||||
|
ai.m_ineq.m_args_value += coeff * (new_value - old_value);
|
||||||
|
rational dtt_new = dtt(ai.m_ineq);
|
||||||
|
bool was_true = clause.is_true();
|
||||||
|
if (dtt_new < clause.m_dts) {
|
||||||
|
if (was_true && clause.m_dts > 0 && dtt_new == 0 && 1 == clause.m_num_trues) {
|
||||||
|
for (auto lit : clause.m_bools) {
|
||||||
|
if (is_true(lit)) {
|
||||||
|
dec_break(lit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clause.m_dts = dtt_new;
|
||||||
|
if (!was_true && clause.is_true())
|
||||||
|
m_unsat.remove(ai.m_clause_idx);
|
||||||
|
}
|
||||||
|
else if (clause.m_dts == dtt_old && dtt_old < dtt_new) {
|
||||||
|
clause.m_dts = dts(clause);
|
||||||
|
if (was_true && !clause.is_true())
|
||||||
|
m_unsat.insert(ai.m_clause_idx);
|
||||||
|
if (was_true && clause.is_true() && clause.m_dts > 0 && dtt_old == 0 && 1 == clause.m_num_trues) {
|
||||||
|
for (auto lit : clause.m_bools) {
|
||||||
|
if (is_true(lit)) {
|
||||||
|
inc_break(lit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SASSERT(clause.m_dts >= 0);
|
||||||
|
}
|
||||||
|
vi.m_value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool solver::sls::flip_arith(clause const& clause) {
|
||||||
|
rational new_value;
|
||||||
|
for (auto a : clause.m_arith) {
|
||||||
|
auto const& ai = m_atoms[a];
|
||||||
|
ineq const& ineq = ai.m_ineq;
|
||||||
|
for (auto const& [coeff, v] : ineq.m_args) {
|
||||||
|
if (!ineq.is_true() && cm(ineq, v, new_value)) {
|
||||||
|
int score = cm_score(v, new_value);
|
||||||
|
if (score <= 0)
|
||||||
|
continue;
|
||||||
|
unsigned num_unsat = m_unsat.size();
|
||||||
|
update(v, new_value);
|
||||||
|
std::cout << "score " << v << " " << score << "\n";
|
||||||
|
std::cout << num_unsat << " -> " << m_unsat.size() << "\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
rational solver::sls::dts(clause const& cl) const {
|
||||||
|
rational d(1), d2;
|
||||||
|
bool first = true;
|
||||||
|
for (auto a : cl.m_arith) {
|
||||||
|
auto const& ai = m_atoms[a];
|
||||||
|
d2 = dtt(ai.m_ineq);
|
||||||
|
if (first)
|
||||||
|
d = d2, first = false;
|
||||||
|
else
|
||||||
|
d = std::min(d, d2);
|
||||||
|
if (d == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
rational solver::sls::dts(clause const& cl, var_t v, rational const& new_value) const {
|
||||||
|
rational d(1), d2;
|
||||||
|
bool first = true;
|
||||||
|
for (auto a : cl.m_arith) {
|
||||||
|
auto const& ai = m_atoms[a];
|
||||||
|
d2 = dtt(ai.m_ineq, v, new_value);
|
||||||
|
if (first)
|
||||||
|
d = d2, first = false;
|
||||||
|
else
|
||||||
|
d = std::min(d, d2);
|
||||||
|
if (d == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c)
|
||||||
|
//
|
||||||
|
rational solver::sls::dscore(var_t v, rational const& new_value) const {
|
||||||
|
auto const& vi = m_vars[v];
|
||||||
|
rational score(0);
|
||||||
|
for (auto const& [coeff, atm] : vi.m_atoms) {
|
||||||
|
auto const& ai = m_atoms[atm];
|
||||||
|
auto const& cl = m_clauses[ai.m_clause_idx];
|
||||||
|
score += (cl.m_dts - dts(cl, v, new_value)) * rational(cl.m_weight);
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
int solver::sls::cm_score(var_t v, rational const& new_value) {
|
||||||
|
int score = 0;
|
||||||
|
auto& vi = m_vars[v];
|
||||||
|
for (auto const& [coeff, atm] : vi.m_atoms) {
|
||||||
|
auto const& ai = m_atoms[atm];
|
||||||
|
auto const& clause = m_clauses[ai.m_clause_idx];
|
||||||
|
rational dtt_old = dtt(ai.m_ineq);
|
||||||
|
rational dtt_new = dtt(ai.m_ineq, v, new_value);
|
||||||
|
if (!clause.is_true()) {
|
||||||
|
if (dtt_new == 0)
|
||||||
|
++score;
|
||||||
|
}
|
||||||
|
else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0)
|
||||||
|
continue;
|
||||||
|
else {
|
||||||
|
bool has_true = false;
|
||||||
|
for (auto a : clause.m_arith) {
|
||||||
|
auto const& ai = m_atoms[a];
|
||||||
|
rational d = dtt(ai.m_ineq, v, new_value);
|
||||||
|
has_true |= (d == 0);
|
||||||
|
}
|
||||||
|
if (!has_true)
|
||||||
|
--score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace arith {
|
||||||
solver::solver(euf::solver& ctx, theory_id id) :
|
solver::solver(euf::solver& ctx, theory_id id) :
|
||||||
th_euf_solver(ctx, symbol("arith"), id),
|
th_euf_solver(ctx, symbol("arith"), id),
|
||||||
m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)),
|
m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)),
|
||||||
|
m_local_search(*this),
|
||||||
m_resource_limit(*this),
|
m_resource_limit(*this),
|
||||||
m_bp(*this),
|
m_bp(*this),
|
||||||
a(m),
|
a(m),
|
||||||
|
|
|
@ -78,12 +78,7 @@ namespace arith {
|
||||||
m_eq_tail++;
|
m_eq_tail++;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
void set_type(euf::solver& ctx, hint_type ty) {
|
void set_type(euf::solver& ctx, hint_type ty);
|
||||||
ctx.push(value_trail<unsigned>(m_eq_tail));
|
|
||||||
ctx.push(value_trail<unsigned>(m_lit_tail));
|
|
||||||
m_ty = ty;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
void set_num_le(unsigned n) { m_num_le = n; }
|
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_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_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); }
|
||||||
|
@ -96,12 +91,9 @@ namespace arith {
|
||||||
}
|
}
|
||||||
std::pair<rational, literal> const& lit(unsigned i) const { return m_literals[i]; }
|
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]; }
|
std::tuple<enode*, enode*, bool> const& eq(unsigned i) const { return m_eqs[i]; }
|
||||||
arith_proof_hint* mk(euf::solver& s) {
|
arith_proof_hint* mk(euf::solver& s);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class solver : public euf::th_euf_solver {
|
class solver : public euf::th_euf_solver {
|
||||||
|
|
||||||
friend struct arith_proof_hint;
|
friend struct arith_proof_hint;
|
||||||
|
@ -144,7 +136,7 @@ namespace arith {
|
||||||
};
|
};
|
||||||
int_hashtable<var_value_hash, var_value_eq> m_model_eqs;
|
int_hashtable<var_value_hash, var_value_eq> m_model_eqs;
|
||||||
|
|
||||||
bool m_new_eq { false };
|
bool m_new_eq = false;
|
||||||
|
|
||||||
|
|
||||||
// temporary values kept during internalization
|
// temporary values kept during internalization
|
||||||
|
@ -198,6 +190,85 @@ namespace arith {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// local search portion for arithmetic
|
||||||
|
class sls {
|
||||||
|
enum class ineq_kind { EQ, LE, LT, NE };
|
||||||
|
enum class var_kind { INT, REAL };
|
||||||
|
typedef unsigned var_t;
|
||||||
|
typedef unsigned atom_t;
|
||||||
|
|
||||||
|
struct stats {
|
||||||
|
unsigned m_num_flips = 0;
|
||||||
|
};
|
||||||
|
// encode args <= bound, args = bound, args < bound
|
||||||
|
struct ineq {
|
||||||
|
vector<std::pair<rational, var_t>> m_args;
|
||||||
|
ineq_kind m_op = ineq_kind::LE;
|
||||||
|
rational m_bound;
|
||||||
|
rational m_args_value;
|
||||||
|
|
||||||
|
bool is_true() const {
|
||||||
|
switch (m_op) {
|
||||||
|
case ineq_kind::LE:
|
||||||
|
return m_args_value <= m_bound;
|
||||||
|
case ineq_kind::EQ:
|
||||||
|
return m_args_value == m_bound;
|
||||||
|
case ineq_kind::NE:
|
||||||
|
return m_args_value != m_bound;
|
||||||
|
default:
|
||||||
|
return m_args_value < m_bound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct var_info {
|
||||||
|
rational m_value;
|
||||||
|
rational m_best_value;
|
||||||
|
var_kind m_kind = var_kind::INT;
|
||||||
|
vector<std::pair<rational, atom_t>> m_atoms;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct atom_info {
|
||||||
|
ineq m_ineq;
|
||||||
|
unsigned m_clause_idx;
|
||||||
|
bool m_is_bool = false;
|
||||||
|
bool m_phase = false;
|
||||||
|
bool m_best_phase = false;
|
||||||
|
unsigned m_breaks = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
solver& s;
|
||||||
|
ast_manager& m;
|
||||||
|
unsigned m_max_arith_steps = 0;
|
||||||
|
stats m_stats;
|
||||||
|
vector<atom_info> m_atoms;
|
||||||
|
vector<var_info> m_vars;
|
||||||
|
|
||||||
|
bool flip();
|
||||||
|
void log() {}
|
||||||
|
bool flip_unsat() { return false; }
|
||||||
|
bool flip_clauses() { return false; }
|
||||||
|
bool flip_dscore() { return false; }
|
||||||
|
// bool flip_dscore(clause const&);
|
||||||
|
// bool flip(clause const&);
|
||||||
|
rational dtt(ineq const& ineq) const { return dtt(ineq.m_args_value, ineq); }
|
||||||
|
rational dtt(rational const& args, ineq const& ineq) const;
|
||||||
|
rational dtt(ineq const& ineq, var_t v, rational const& new_value) const;
|
||||||
|
// rational dts(clause const& cl, var_t v, rational const& new_value) const;
|
||||||
|
// rational dts(clause const& cl) const;
|
||||||
|
bool cm(ineq const& ineq, var_t v, rational& new_value);
|
||||||
|
|
||||||
|
rational value(var_t v) const { return m_vars[v].m_value; }
|
||||||
|
public:
|
||||||
|
sls(solver& s);
|
||||||
|
void operator ()(bool_vector& phase);
|
||||||
|
void set_bounds_begin();
|
||||||
|
void set_bounds_end(unsigned num_literals);
|
||||||
|
void set_bounds(enode* n);
|
||||||
|
};
|
||||||
|
|
||||||
|
sls m_local_search;
|
||||||
|
|
||||||
typedef vector<std::pair<rational, lpvar>> var_coeffs;
|
typedef vector<std::pair<rational, lpvar>> var_coeffs;
|
||||||
vector<rational> m_columns;
|
vector<rational> m_columns;
|
||||||
var_coeffs m_left_side; // constraint left side
|
var_coeffs m_left_side; // constraint left side
|
||||||
|
@ -233,10 +304,10 @@ namespace arith {
|
||||||
unsigned m_asserted_qhead = 0;
|
unsigned m_asserted_qhead = 0;
|
||||||
|
|
||||||
svector<std::pair<theory_var, theory_var> > m_assume_eq_candidates;
|
svector<std::pair<theory_var, theory_var> > m_assume_eq_candidates;
|
||||||
unsigned m_assume_eq_head{ 0 };
|
unsigned m_assume_eq_head = 0;
|
||||||
lp::u_set m_tmp_var_set;
|
lp::u_set m_tmp_var_set;
|
||||||
|
|
||||||
unsigned m_num_conflicts{ 0 };
|
unsigned m_num_conflicts = 0;
|
||||||
lp_api::stats m_stats;
|
lp_api::stats m_stats;
|
||||||
svector<scope> m_scopes;
|
svector<scope> m_scopes;
|
||||||
|
|
||||||
|
@ -515,6 +586,11 @@ namespace arith {
|
||||||
bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); }
|
bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); }
|
||||||
bool has_unhandled() const override { return m_not_handled != nullptr; }
|
bool has_unhandled() const override { return m_not_handled != nullptr; }
|
||||||
|
|
||||||
|
void set_bounds_begin() override { m_local_search.set_bounds_begin(); }
|
||||||
|
void set_bounds_end(unsigned num_literals) override { m_local_search.set_bounds_end(num_literals); }
|
||||||
|
void set_bounds(enode* n) override { m_local_search.set_bounds(n); }
|
||||||
|
void local_search(bool_vector& phase) override { m_local_search(phase); }
|
||||||
|
|
||||||
// bounds and equality propagation callbacks
|
// bounds and equality propagation callbacks
|
||||||
lp::lar_solver& lp() { return *m_solver; }
|
lp::lar_solver& lp() { return *m_solver; }
|
||||||
lp::lar_solver const& lp() const { return *m_solver; }
|
lp::lar_solver const& lp() const { return *m_solver; }
|
||||||
|
@ -523,4 +599,7 @@ namespace arith {
|
||||||
void consume(rational const& v, lp::constraint_index j);
|
void consume(rational const& v, lp::constraint_index j);
|
||||||
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
|
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
126
src/sat/smt/euf_local_search.cpp
Normal file
126
src/sat/smt/euf_local_search.cpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2020 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
euf_local_search.cpp
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Local search dispatch for SMT
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2023-02-07
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#include "sat/sat_solver.h"
|
||||||
|
#include "sat/sat_ddfw.h"
|
||||||
|
#include "sat/smt/euf_solver.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace euf {
|
||||||
|
|
||||||
|
void solver::local_search(bool_vector& phase) {
|
||||||
|
|
||||||
|
|
||||||
|
scoped_limits scoped_rl(m.limit());
|
||||||
|
sat::ddfw bool_search;
|
||||||
|
bool_search.add(s());
|
||||||
|
bool_search.updt_params(s().params());
|
||||||
|
bool_search.set_seed(rand());
|
||||||
|
scoped_rl.push_child(&(bool_search.rlimit()));
|
||||||
|
|
||||||
|
unsigned rounds = 0;
|
||||||
|
unsigned max_rounds = 30;
|
||||||
|
|
||||||
|
sat::model mdl(s().num_vars());
|
||||||
|
for (unsigned v = 0; v < s().num_vars(); ++v)
|
||||||
|
mdl[v] = s().value(v);
|
||||||
|
|
||||||
|
|
||||||
|
while (m.inc() && rounds < max_rounds) {
|
||||||
|
setup_bounds(mdl);
|
||||||
|
bool_search.reinit(s(), phase);
|
||||||
|
|
||||||
|
// Non-boolean literals are assumptions to Boolean search
|
||||||
|
literal_vector _lits;
|
||||||
|
for (unsigned v = 0; v < mdl.size(); ++v)
|
||||||
|
if (!is_propositional(literal(v)))
|
||||||
|
_lits.push_back(literal(v, mdl[v] == l_false));
|
||||||
|
|
||||||
|
bool_search.rlimit().push(m_max_bool_steps);
|
||||||
|
|
||||||
|
lbool r = bool_search.check(_lits.size(), _lits.data(), nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
auto const& mdl = bool_search.get_model();
|
||||||
|
for (unsigned i = 0; i < mdl.size(); ++i)
|
||||||
|
phase[i] = mdl[i] == l_true;
|
||||||
|
|
||||||
|
for (auto* th : m_solvers)
|
||||||
|
th->local_search(phase);
|
||||||
|
++rounds;
|
||||||
|
// if is_sat break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool solver::is_propositional(sat::literal lit) {
|
||||||
|
expr* e = m_bool_var2expr.get(lit.var(), nullptr);
|
||||||
|
if (!e)
|
||||||
|
return true;
|
||||||
|
if (is_uninterp_const(e))
|
||||||
|
return true;
|
||||||
|
euf::enode* n = m_egraph.find(e);
|
||||||
|
if (!n)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void solver::setup_bounds(sat::model const& mdl) {
|
||||||
|
unsigned num_literals = 0;
|
||||||
|
unsigned num_bool = 0;
|
||||||
|
for (auto* th : m_solvers)
|
||||||
|
th->set_bounds_begin();
|
||||||
|
|
||||||
|
auto init_literal = [&](sat::literal l) {
|
||||||
|
if (is_propositional(l)) {
|
||||||
|
++num_bool;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
euf::enode* n = m_egraph.find(m_bool_var2expr.get(l.var(), nullptr));
|
||||||
|
for (auto const& thv : enode_th_vars(n)) {
|
||||||
|
auto* th = m_id2solver.get(thv.get_id(), nullptr);
|
||||||
|
if (th)
|
||||||
|
th->set_bounds(n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto is_true = [&](auto lit) {
|
||||||
|
return mdl[lit.var()] == to_lbool(!lit.sign());
|
||||||
|
};
|
||||||
|
|
||||||
|
svector<sat::solver::bin_clause> bin_clauses;
|
||||||
|
s().collect_bin_clauses(bin_clauses, false, false);
|
||||||
|
for (auto* cp : s().clauses()) {
|
||||||
|
if (any_of(*cp, [&](auto lit) { return is_true(lit); }))
|
||||||
|
continue;
|
||||||
|
num_literals += cp->size();
|
||||||
|
for (auto l : *cp)
|
||||||
|
init_literal(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto [l1, l2] : bin_clauses) {
|
||||||
|
if (is_true(l1) || is_true(l2))
|
||||||
|
continue;
|
||||||
|
num_literals += 2;
|
||||||
|
init_literal(l1);
|
||||||
|
init_literal(l2);
|
||||||
|
};
|
||||||
|
|
||||||
|
m_max_bool_steps = (m_ls_config.L * num_bool) / num_literals;
|
||||||
|
|
||||||
|
for (auto* th : m_solvers)
|
||||||
|
th->set_bounds_end(num_literals);
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,6 +100,14 @@ namespace euf {
|
||||||
scope(unsigned l) : m_var_lim(l) {}
|
scope(unsigned l) : m_var_lim(l) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct local_search_config {
|
||||||
|
double cb = 0.0;
|
||||||
|
unsigned L = 20;
|
||||||
|
unsigned t = 45;
|
||||||
|
unsigned max_no_improve = 500000;
|
||||||
|
double sp = 0.0003;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast<size_t*>((size_t)(l.index() << 4)), 1); }
|
size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast<size_t*>((size_t)(l.index() << 4)), 1); }
|
||||||
size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); }
|
size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); }
|
||||||
|
@ -119,6 +127,7 @@ namespace euf {
|
||||||
sat::sat_internalizer& si;
|
sat::sat_internalizer& si;
|
||||||
relevancy m_relevancy;
|
relevancy m_relevancy;
|
||||||
smt_params m_config;
|
smt_params m_config;
|
||||||
|
local_search_config m_ls_config;
|
||||||
euf::egraph m_egraph;
|
euf::egraph m_egraph;
|
||||||
trail_stack m_trail;
|
trail_stack m_trail;
|
||||||
stats m_stats;
|
stats m_stats;
|
||||||
|
@ -253,6 +262,11 @@ namespace euf {
|
||||||
constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); }
|
constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); }
|
||||||
constraint& lit_constraint(enode* n);
|
constraint& lit_constraint(enode* n);
|
||||||
|
|
||||||
|
// local search
|
||||||
|
unsigned m_max_bool_steps = 10;
|
||||||
|
bool is_propositional(sat::literal lit);
|
||||||
|
void setup_bounds(sat::model const& mdl);
|
||||||
|
|
||||||
// user propagator
|
// user propagator
|
||||||
void check_for_user_propagator() {
|
void check_for_user_propagator() {
|
||||||
if (!m_user_propagator)
|
if (!m_user_propagator)
|
||||||
|
@ -339,6 +353,7 @@ namespace euf {
|
||||||
void add_assumptions(sat::literal_set& assumptions) override;
|
void add_assumptions(sat::literal_set& assumptions) override;
|
||||||
bool tracking_assumptions() override;
|
bool tracking_assumptions() override;
|
||||||
std::string reason_unknown() override { return m_reason_unknown; }
|
std::string reason_unknown() override { return m_reason_unknown; }
|
||||||
|
void local_search(bool_vector& phase) override;
|
||||||
|
|
||||||
void propagate(literal lit, ext_justification_idx idx);
|
void propagate(literal lit, ext_justification_idx idx);
|
||||||
bool propagate(enode* a, enode* b, ext_justification_idx idx);
|
bool propagate(enode* a, enode* b, ext_justification_idx idx);
|
||||||
|
@ -551,4 +566,3 @@ namespace euf {
|
||||||
inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) {
|
inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) {
|
||||||
return s.display(out);
|
return s.display(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,15 @@ namespace euf {
|
||||||
|
|
||||||
sat::status status() const { return sat::status::th(false, get_id()); }
|
sat::status status() const { return sat::status::th(false, get_id()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local search interface
|
||||||
|
*/
|
||||||
|
virtual void set_bounds_begin() {}
|
||||||
|
|
||||||
|
virtual void set_bounds_end(unsigned num_literals) {}
|
||||||
|
|
||||||
|
virtual void set_bounds(enode* n) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class th_proof_hint : public sat::proof_hint {
|
class th_proof_hint : public sat::proof_hint {
|
||||||
|
|
Loading…
Reference in a new issue