mirror of
https://github.com/Z3Prover/z3
synced 2025-04-13 20:38:43 +00:00
wip - local search for euf/arithmetic
This commit is contained in:
parent
46c8d78ece
commit
7bef2f3e6f
|
@ -49,6 +49,7 @@ namespace sat {
|
|||
else if (should_parallel_sync()) do_parallel_sync();
|
||||
else shift_weights();
|
||||
}
|
||||
remove_assumptions();
|
||||
log();
|
||||
return m_min_sz == 0 ? l_true : l_undef;
|
||||
}
|
||||
|
@ -244,7 +245,6 @@ namespace sat {
|
|||
m_use_list_index.push_back(m_flat_use_list.size());
|
||||
}
|
||||
|
||||
|
||||
void ddfw::flip(bool_var v) {
|
||||
++m_flips;
|
||||
literal lit = literal(v, !value(v));
|
||||
|
@ -309,19 +309,15 @@ namespace sat {
|
|||
log();
|
||||
|
||||
if (m_reinit_count % 2 == 0) {
|
||||
for (auto& ci : m_clauses) {
|
||||
ci.m_weight += 1;
|
||||
}
|
||||
for (auto& ci : m_clauses)
|
||||
ci.m_weight += 1;
|
||||
}
|
||||
else {
|
||||
for (auto& ci : m_clauses) {
|
||||
if (ci.is_true()) {
|
||||
ci.m_weight = m_config.m_init_clause_weight;
|
||||
}
|
||||
else {
|
||||
ci.m_weight = m_config.m_init_clause_weight + 1;
|
||||
}
|
||||
}
|
||||
for (auto& ci : m_clauses)
|
||||
if (ci.is_true())
|
||||
ci.m_weight = m_config.m_init_clause_weight;
|
||||
else
|
||||
ci.m_weight = m_config.m_init_clause_weight + 1;
|
||||
}
|
||||
init_clause_data();
|
||||
++m_reinit_count;
|
||||
|
@ -341,11 +337,9 @@ namespace sat {
|
|||
clause const& c = get_clause(i);
|
||||
ci.m_trues = 0;
|
||||
ci.m_num_trues = 0;
|
||||
for (literal lit : c) {
|
||||
if (is_true(lit)) {
|
||||
ci.add(lit);
|
||||
}
|
||||
}
|
||||
for (literal lit : c)
|
||||
if (is_true(lit))
|
||||
ci.add(lit);
|
||||
switch (ci.m_num_trues) {
|
||||
case 0:
|
||||
for (literal lit : c) {
|
||||
|
@ -384,12 +378,10 @@ namespace sat {
|
|||
void ddfw::reinit_values() {
|
||||
for (unsigned i = 0; i < num_vars(); ++i) {
|
||||
int b = bias(i);
|
||||
if (0 == (m_rand() % (1 + abs(b)))) {
|
||||
value(i) = (m_rand() % 2) == 0;
|
||||
}
|
||||
else {
|
||||
value(i) = bias(i) > 0;
|
||||
}
|
||||
if (0 == (m_rand() % (1 + abs(b))))
|
||||
value(i) = (m_rand() % 2) == 0;
|
||||
else
|
||||
value(i) = bias(i) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,17 @@ namespace sat {
|
|||
void add(literal lit) { ++m_num_trues; m_trues += lit.index(); }
|
||||
void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); }
|
||||
};
|
||||
|
||||
class use_list {
|
||||
ddfw& p;
|
||||
unsigned i;
|
||||
public:
|
||||
use_list(ddfw& p, literal lit) :
|
||||
p(p), i(lit.index()) {}
|
||||
unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; }
|
||||
unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
struct config {
|
||||
|
@ -102,15 +113,7 @@ namespace sat {
|
|||
|
||||
parallel* m_par;
|
||||
|
||||
class use_list {
|
||||
ddfw& p;
|
||||
unsigned i;
|
||||
public:
|
||||
use_list(ddfw& p, literal lit):
|
||||
p(p), i(lit.index()) {}
|
||||
unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; }
|
||||
unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; }
|
||||
};
|
||||
|
||||
|
||||
void flatten_use_list();
|
||||
|
||||
|
@ -163,7 +166,6 @@ namespace sat {
|
|||
// flip activity
|
||||
bool do_flip();
|
||||
bool_var pick_var();
|
||||
void flip(bool_var v);
|
||||
void save_best_values();
|
||||
void save_model();
|
||||
void save_priorities();
|
||||
|
@ -245,6 +247,10 @@ namespace sat {
|
|||
|
||||
void remove_assumptions();
|
||||
|
||||
void flip(bool_var v);
|
||||
|
||||
use_list get_use_list(literal lit) { return use_list(*this, lit); }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -353,10 +353,7 @@ namespace sat {
|
|||
DEBUG_CODE(verify_unsat_stack(););
|
||||
}
|
||||
|
||||
local_search::local_search() :
|
||||
m_is_unsat(false),
|
||||
m_initializing(false),
|
||||
m_par(nullptr) {
|
||||
local_search::local_search() {
|
||||
}
|
||||
|
||||
void local_search::reinit(solver& s, bool_vector const& phase) {
|
||||
|
@ -375,11 +372,10 @@ namespace sat {
|
|||
m_vars.reserve(s.num_vars());
|
||||
m_config.set_config(s.get_config());
|
||||
|
||||
if (m_config.phase_sticky()) {
|
||||
unsigned v = 0;
|
||||
unsigned v = 0;
|
||||
if (m_config.phase_sticky())
|
||||
for (var_info& vi : m_vars)
|
||||
vi.m_bias = s.m_phase[v++] ? 98 : 2;
|
||||
}
|
||||
vi.m_bias = s.m_phase[v++] ? 98 : 2;
|
||||
|
||||
// copy units
|
||||
unsigned trail_sz = s.init_trail_size();
|
||||
|
@ -419,9 +415,8 @@ namespace sat {
|
|||
if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb)))
|
||||
throw default_exception("local search is incomplete with extensions beyond PB");
|
||||
|
||||
if (_init) {
|
||||
init();
|
||||
}
|
||||
if (_init)
|
||||
init();
|
||||
}
|
||||
|
||||
local_search::~local_search() {
|
||||
|
|
|
@ -133,21 +133,21 @@ namespace sat {
|
|||
vector<constraint> m_constraints; // all constraints
|
||||
literal_vector m_assumptions; // temporary assumptions
|
||||
literal_vector m_prop_queue; // propagation queue
|
||||
unsigned m_num_non_binary_clauses;
|
||||
bool m_is_pb;
|
||||
bool m_is_unsat;
|
||||
unsigned m_num_non_binary_clauses = 0;
|
||||
bool m_is_pb = false;
|
||||
bool m_is_unsat = false;
|
||||
unsigned_vector m_unsat_stack; // store all the unsat constraints
|
||||
unsigned_vector m_index_in_unsat_stack; // which position is a constraint in the unsat_stack
|
||||
|
||||
// configuration changed decreasing variables (score>0 and conf_change==true)
|
||||
bool_var_vector m_goodvar_stack;
|
||||
bool m_initializing;
|
||||
bool m_initializing = false;
|
||||
|
||||
|
||||
// information about solution
|
||||
unsigned m_best_unsat;
|
||||
double m_best_unsat_rate;
|
||||
double m_last_best_unsat_rate;
|
||||
unsigned m_best_unsat = 0;
|
||||
double m_best_unsat_rate = 0;
|
||||
double m_last_best_unsat_rate = 0;
|
||||
// for non-known instance, set as maximal
|
||||
int m_best_known_value = INT_MAX; // best known value for this instance
|
||||
|
||||
|
@ -159,7 +159,7 @@ namespace sat {
|
|||
|
||||
reslimit m_limit;
|
||||
random_gen m_rand;
|
||||
parallel* m_par;
|
||||
parallel* m_par = nullptr;
|
||||
model m_model;
|
||||
|
||||
inline int score(bool_var v) const { return m_vars[v].m_score; }
|
||||
|
|
|
@ -3,7 +3,7 @@ z3_add_component(sat_smt
|
|||
arith_axioms.cpp
|
||||
arith_diagnostics.cpp
|
||||
arith_internalize.cpp
|
||||
arith_local_search.cpp
|
||||
arith_sls.cpp
|
||||
arith_solver.cpp
|
||||
array_axioms.cpp
|
||||
array_diagnostics.cpp
|
||||
|
|
511
src/sat/smt/arith_sls.cpp
Normal file
511
src/sat/smt/arith_sls.cpp
Normal file
|
@ -0,0 +1,511 @@
|
|||
/*++
|
||||
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 to initialize ineqs (arithmetical atoms)
|
||||
///
|
||||
|
||||
|
||||
sls::sls(solver& s):
|
||||
s(s), m(s.m) {}
|
||||
|
||||
void sls::operator()(bool_vector& phase) {
|
||||
|
||||
m_best_min_unsat = unsat().size();
|
||||
unsigned num_steps = 0;
|
||||
while (m.inc() && m_best_min_unsat > 0 && num_steps < m_max_arith_steps) {
|
||||
if (!flip())
|
||||
break;
|
||||
++m_stats.m_num_flips;
|
||||
++num_steps;
|
||||
unsigned num_unsat = unsat().size();
|
||||
if (num_unsat < m_best_min_unsat) {
|
||||
m_best_min_unsat = num_unsat;
|
||||
num_steps = 0;
|
||||
save_best_values();
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "(sls " << m_stats.m_num_flips << " " << unsat().size() << ")\n");
|
||||
}
|
||||
|
||||
void sls::save_best_values() {
|
||||
// first compute assignment to terms
|
||||
// then update non-basic variables in tableau, assuming a sat solution was found.
|
||||
#if false
|
||||
for (auto const& [t, v] : terms) {
|
||||
rational val;
|
||||
lp::lar_term const& term = lp().get_term(t);
|
||||
for (lp::lar_term::ival arg : term) {
|
||||
auto t2 = lp().column2tv(arg.column());
|
||||
auto w = lp().local_to_external(t2.id());
|
||||
val += arg.coeff() * local_search.value(w);
|
||||
}
|
||||
update(v, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
if (s.is_bool(v))
|
||||
continue;
|
||||
if (!s.lp().external_is_used(v))
|
||||
continue;
|
||||
rational old_value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero();
|
||||
rational new_value = value(v);
|
||||
if (old_value == new_value)
|
||||
continue;
|
||||
s.ensure_column(v);
|
||||
lp::column_index vj = s.lp().to_column_index(v);
|
||||
SASSERT(!vj.is_null());
|
||||
if (!s.lp().is_base(vj.index())) {
|
||||
lp::impq val(new_value);
|
||||
s.lp().set_value_for_nbasic_column(vj.index(), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sls::set(sat::ddfw* d) {
|
||||
m_bool_search = d;
|
||||
add_vars();
|
||||
m_clauses.resize(d->num_clauses());
|
||||
for (unsigned i = 0; i < d->num_clauses(); ++i)
|
||||
for (sat::literal lit : *d->get_clause_info(i).m_clause)
|
||||
init_literal(lit);
|
||||
}
|
||||
|
||||
void sls::set_bounds_begin() {
|
||||
m_max_arith_steps = 0;
|
||||
}
|
||||
|
||||
void sls::set_bounds(enode* n) {
|
||||
++m_max_arith_steps;
|
||||
}
|
||||
|
||||
void sls::set_bounds_end(unsigned num_literals) {
|
||||
m_max_arith_steps = (m_config.L * m_max_arith_steps) / num_literals;
|
||||
}
|
||||
|
||||
bool sls::flip() {
|
||||
log();
|
||||
return flip_unsat() || flip_clauses() || flip_dscore();
|
||||
}
|
||||
|
||||
// distance to true
|
||||
rational 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:
|
||||
if (args < ineq.m_bound)
|
||||
return rational::zero();
|
||||
return args - ineq.m_bound + 1;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return rational::zero();
|
||||
}
|
||||
}
|
||||
|
||||
rational 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 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;
|
||||
}
|
||||
|
||||
bool sls::flip_unsat() {
|
||||
unsigned start = s.random();
|
||||
unsigned sz = unsat().size();
|
||||
for (unsigned i = sz; i-- > 0; )
|
||||
if (flip(unsat().elem_at((i + start) % sz)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sls::flip(unsigned cl) {
|
||||
auto const& clause = get_clause(cl);
|
||||
rational new_value;
|
||||
for (literal lit : clause) {
|
||||
auto const* ineq = atom(lit);
|
||||
if (!ineq || ineq->is_true())
|
||||
continue;
|
||||
for (auto const& [coeff, v] : ineq->m_args) {
|
||||
if (!cm(*ineq, v, new_value))
|
||||
continue;
|
||||
int score = cm_score(v, new_value);
|
||||
if (score <= 0)
|
||||
continue;
|
||||
unsigned num_unsat = unsat().size();
|
||||
update(v, new_value);
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream() << "score " << v << " " << score << "\n"
|
||||
<< num_unsat << " -> " << unsat().size() << "\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sls::flip_clauses() {
|
||||
unsigned start = s.random();
|
||||
unsigned sz = m_bool_search->num_clauses();
|
||||
for (unsigned i = sz; i-- > 0; )
|
||||
if (flip((i + start) % sz))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sls::flip_dscore() {
|
||||
paws();
|
||||
unsigned start = s.random();
|
||||
unsigned sz = unsat().size();
|
||||
for (unsigned i = sz; i-- > 0; )
|
||||
if (flip_dscore(unsat().elem_at((i + start) % sz)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sls::flip_dscore(unsigned cl) {
|
||||
auto const& clause = get_clause(cl);
|
||||
rational new_value, min_value, min_score(-1);
|
||||
var_t min_var = UINT_MAX;
|
||||
for (auto lit : clause) {
|
||||
auto const* ineq = atom(lit);
|
||||
if (!ineq || ineq->is_true())
|
||||
continue;
|
||||
for (auto const& [coeff, v] : ineq->m_args) {
|
||||
if (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;
|
||||
}
|
||||
|
||||
/**
|
||||
* redistribute weights of clauses. TODO - re-use ddfw weights instead.
|
||||
*/
|
||||
void sls::paws() {
|
||||
for (unsigned cl = num_clauses(); cl-- > 0; ) {
|
||||
auto& clause = get_clause_info(cl);
|
||||
bool above = 10000 * m_config.sp <= (s.random() % 10000);
|
||||
if (!above && clause.is_true() && get_weight(cl) > 1)
|
||||
get_weight(cl) -= 1;
|
||||
if (above && !clause.is_true())
|
||||
get_weight(cl) += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c)
|
||||
//
|
||||
rational sls::dscore(var_t v, rational const& new_value) const {
|
||||
auto const& vi = m_vars[v];
|
||||
rational score(0);
|
||||
for (auto const& [coeff, lit] : vi.m_literals)
|
||||
for (auto cl : m_bool_search->get_use_list(lit))
|
||||
score += (dts(cl) - dts(cl, v, new_value)) * rational(get_weight(cl));
|
||||
return score;
|
||||
}
|
||||
|
||||
int sls::cm_score(var_t v, rational const& new_value) {
|
||||
int score = 0;
|
||||
auto& vi = m_vars[v];
|
||||
for (auto const& [coeff, lit] : vi.m_literals) {
|
||||
auto const& ineq = *atom(lit);
|
||||
rational dtt_old = dtt(ineq);
|
||||
rational dtt_new = dtt(ineq, v, new_value);
|
||||
for (auto cl : m_bool_search->get_use_list(lit)) {
|
||||
auto const& clause = get_clause_info(cl);
|
||||
if (!clause.is_true()) {
|
||||
if (dtt_new == 0)
|
||||
++score; // false -> true
|
||||
}
|
||||
else if (dtt_new == 0 || dtt_old > 0 || clause.m_num_trues > 0) // true -> true ?? TODO
|
||||
continue;
|
||||
else if (all_of(*clause.m_clause, [&](auto lit) { return !atom(lit) || dtt(*atom(lit), v, new_value) > 0; })) // ?? TODO
|
||||
--score;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
rational sls::dts(unsigned cl) const {
|
||||
rational d(1), d2;
|
||||
bool first = true;
|
||||
for (auto a : get_clause(cl)) {
|
||||
auto const* ineq = atom(a);
|
||||
if (!ineq)
|
||||
continue;
|
||||
d2 = dtt(*ineq);
|
||||
if (first)
|
||||
d = d2, first = false;
|
||||
else
|
||||
d = std::min(d, d2);
|
||||
if (d == 0)
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
rational sls::dts(unsigned cl, var_t v, rational const& new_value) const {
|
||||
rational d(1), d2;
|
||||
bool first = true;
|
||||
for (auto lit : get_clause(cl)) {
|
||||
auto const* ineq = atom(lit);
|
||||
if (!ineq)
|
||||
continue;
|
||||
d2 = dtt(*ineq, v, new_value);
|
||||
if (first)
|
||||
d = d2, first = false;
|
||||
else
|
||||
d = std::min(d, d2);
|
||||
if (d == 0)
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void 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, lit] : vi.m_literals) {
|
||||
auto& ineq = *atom(lit);
|
||||
rational dtt_old = dtt(ineq);
|
||||
ineq.m_args_value += coeff * (new_value - old_value);
|
||||
rational dtt_new = dtt(ineq);
|
||||
SASSERT(!(dtt_new == 0 && dtt_new < dtt_old) || m_bool_search->get_value(lit.var()) == lit.sign());
|
||||
SASSERT(!(dtt_old == 0 && dtt_new > dtt_old) || m_bool_search->get_value(lit.var()) != lit.sign());
|
||||
if (dtt_new == 0 && dtt_new < dtt_old) // flip from false to true
|
||||
m_bool_search->flip(lit.var());
|
||||
else if (dtt_old == 0 && dtt_old < dtt_new) // flip from true to false
|
||||
m_bool_search->flip(lit.var());
|
||||
dtt(ineq) = dtt_new;
|
||||
SASSERT((dtt_new == 0) == (m_bool_search->get_value(lit.var()) != lit.sign()));
|
||||
}
|
||||
vi.m_value = new_value;
|
||||
}
|
||||
|
||||
void sls::add_vars() {
|
||||
SASSERT(m_vars.empty());
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
rational value = s.is_registered_var(v) ? s.get_ivalue(v).x : rational::zero();
|
||||
value = s.is_int(v) ? ceil(value) : value;
|
||||
auto k = s.is_int(v) ? sls::var_kind::INT : sls::var_kind::REAL;
|
||||
m_vars.push_back({ value, value, k, {} });
|
||||
}
|
||||
}
|
||||
|
||||
sls::ineq& sls::new_ineq(ineq_kind op, rational const& bound) {
|
||||
auto* i = alloc(ineq);
|
||||
i->m_bound = bound;
|
||||
i->m_op = op;
|
||||
return *i;
|
||||
}
|
||||
|
||||
void sls::add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v) {
|
||||
ineq.m_args.push_back({ c, v });
|
||||
ineq.m_args_value += c * value(v);
|
||||
m_vars[v].m_literals.push_back({ c, lit });
|
||||
}
|
||||
|
||||
void sls::add_bounds(sat::literal_vector& bounds) {
|
||||
unsigned bvars = s.s().num_vars();
|
||||
auto add_ineq = [&](sat::literal lit, ineq& i) {
|
||||
m_literals.set(lit.index(), &i);
|
||||
bounds.push_back(lit);
|
||||
};
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
rational lo, hi;
|
||||
bool is_strict_lo = false, is_strict_hi = false;
|
||||
lp::constraint_index ci;
|
||||
if (!s.is_registered_var(v))
|
||||
continue;
|
||||
lp::column_index vi = s.lp().to_column_index(v);
|
||||
if (vi.is_null())
|
||||
continue;
|
||||
bool has_lo = s.lp().has_lower_bound(vi.index(), ci, lo, is_strict_lo);
|
||||
bool has_hi = s.lp().has_upper_bound(vi.index(), ci, hi, is_strict_hi);
|
||||
|
||||
if (has_lo && has_hi && lo == hi) {
|
||||
auto& ineq = new_ineq(sls::ineq_kind::EQ, lo);
|
||||
sat::literal lit(bvars++);
|
||||
add_arg(lit, ineq, rational::one(), v);
|
||||
add_ineq(lit, ineq);
|
||||
continue;
|
||||
}
|
||||
if (has_lo) {
|
||||
auto& ineq = new_ineq(is_strict_lo ? sls::ineq_kind::LT : sls::ineq_kind::LE, -lo);
|
||||
sat::literal lit(bvars++);
|
||||
add_arg(lit, ineq, -rational::one(), v);
|
||||
add_ineq(lit, ineq);
|
||||
}
|
||||
if (has_hi) {
|
||||
auto& ineq = new_ineq(is_strict_hi ? sls::ineq_kind::LT : sls::ineq_kind::LE, hi);
|
||||
sat::literal lit(bvars++);
|
||||
add_arg(lit, ineq, rational::one(), v);
|
||||
add_ineq(lit, ineq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sls::add_args(ineq& ineq, lp::tv t, theory_var v, rational sign) {
|
||||
if (t.is_term()) {
|
||||
lp::lar_term const& term = s.lp().get_term(t);
|
||||
|
||||
for (lp::lar_term::ival arg : term) {
|
||||
auto t2 = s.lp().column2tv(arg.column());
|
||||
auto w = s.lp().local_to_external(t2.id());
|
||||
ineq.m_args.push_back({ sign * arg.coeff(), w });
|
||||
}
|
||||
}
|
||||
else
|
||||
ineq.m_args.push_back({ sign, s.lp().local_to_external(t.id()) });
|
||||
}
|
||||
|
||||
|
||||
void sls::init_literal(sat::literal lit) {
|
||||
if (m_literals.get(lit.index(), nullptr))
|
||||
return;
|
||||
api_bound* b = nullptr;
|
||||
s.m_bool_var2bound.find(lit.var(), b);
|
||||
if (b) {
|
||||
auto t = b->tv();
|
||||
rational bound = b->get_value();
|
||||
bool should_minus = false;
|
||||
sls::ineq_kind op;
|
||||
if (!lit.sign()) {
|
||||
should_minus = b->get_bound_kind() == lp::GE;
|
||||
op = sls::ineq_kind::LE;
|
||||
}
|
||||
else {
|
||||
should_minus = b->get_bound_kind() == lp::LE;
|
||||
if (s.is_int(b->get_var())) {
|
||||
bound -= 1;
|
||||
op = sls::ineq_kind::LE;
|
||||
}
|
||||
else
|
||||
op = sls::ineq_kind::LT;
|
||||
|
||||
}
|
||||
if (should_minus)
|
||||
bound.neg();
|
||||
auto& ineq = new_ineq(op, bound);
|
||||
|
||||
add_args(ineq, t, b->get_var(), should_minus ? rational::minus_one() :rational::one());
|
||||
set_literal(lit, ineq);
|
||||
return;
|
||||
}
|
||||
|
||||
expr* e = s.bool_var2expr(lit.var());
|
||||
expr* l = nullptr, * r = nullptr;
|
||||
if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) {
|
||||
theory_var u = s.get_th_var(l);
|
||||
theory_var v = s.get_th_var(r);
|
||||
lp::tv tu = s.get_tv(u);
|
||||
lp::tv tv = s.get_tv(v);
|
||||
auto& ineq = new_ineq(lit.sign() ? sls::ineq_kind::NE : sls::ineq_kind::EQ, rational::zero());
|
||||
add_args(ineq, tu, u, rational::one());
|
||||
add_args(ineq, tv, v, -rational::one());
|
||||
set_literal(lit, ineq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate literal with inequality and synchronize truth assignment based on arithmetic values.
|
||||
*/
|
||||
void sls::set_literal(sat::literal lit, ineq& ineq) {
|
||||
m_literals.set(lit.index(), &ineq);
|
||||
if (m_bool_search->get_value(lit.var())) {
|
||||
if (dtt(ineq) != 0)
|
||||
m_bool_search->flip(lit.var());
|
||||
}
|
||||
else {
|
||||
if (dtt(ineq) == 0)
|
||||
m_bool_search->flip(lit.var());
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
147
src/sat/smt/arith_sls.h
Normal file
147
src/sat/smt/arith_sls.h
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*++
|
||||
Copyright (c) 2020 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
arith_local_search.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Theory plugin for arithmetic local search
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2020-09-08
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/obj_pair_set.h"
|
||||
#include "ast/ast_trail.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "math/lp/lp_solver.h"
|
||||
#include "math/lp/lp_primal_simplex.h"
|
||||
#include "math/lp/lp_dual_simplex.h"
|
||||
#include "math/lp/indexed_value.h"
|
||||
#include "math/lp/lar_solver.h"
|
||||
#include "math/lp/nla_solver.h"
|
||||
#include "math/lp/lp_types.h"
|
||||
#include "math/lp/lp_api.h"
|
||||
#include "math/polynomial/algebraic_numbers.h"
|
||||
#include "math/polynomial/polynomial.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
|
||||
namespace arith {
|
||||
|
||||
class solver;
|
||||
|
||||
// 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 config {
|
||||
double cb = 0.0;
|
||||
unsigned L = 20;
|
||||
unsigned t = 45;
|
||||
unsigned max_no_improve = 500000;
|
||||
double sp = 0.0003;
|
||||
};
|
||||
|
||||
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, sat::literal>> m_literals;
|
||||
};
|
||||
|
||||
struct clause {
|
||||
unsigned m_weight = 1;
|
||||
};
|
||||
|
||||
solver& s;
|
||||
ast_manager& m;
|
||||
sat::ddfw* m_bool_search = nullptr;
|
||||
unsigned m_max_arith_steps = 0;
|
||||
unsigned m_best_min_unsat = UINT_MAX;
|
||||
stats m_stats;
|
||||
config m_config;
|
||||
scoped_ptr_vector<ineq> m_literals;
|
||||
vector<var_info> m_vars;
|
||||
vector<clause> m_clauses;
|
||||
|
||||
indexed_uint_set& unsat() { return m_bool_search->unsat_set(); }
|
||||
unsigned num_clauses() const { return m_bool_search->num_clauses(); }
|
||||
sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; }
|
||||
sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; }
|
||||
sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); }
|
||||
sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); }
|
||||
|
||||
ineq* atom(sat::literal lit) const { return m_literals[lit.index()]; }
|
||||
unsigned& get_weight(unsigned idx) { return m_clauses[idx].m_weight; }
|
||||
unsigned get_weight(unsigned idx) const { return m_clauses[idx].m_weight; }
|
||||
bool flip();
|
||||
void log() {}
|
||||
bool flip_unsat();
|
||||
bool flip_clauses();
|
||||
bool flip_dscore();
|
||||
bool flip_dscore(unsigned cl);
|
||||
bool flip(unsigned cl);
|
||||
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(unsigned cl, var_t v, rational const& new_value) const;
|
||||
rational dts(unsigned cl) const;
|
||||
bool cm(ineq const& ineq, var_t v, rational& new_value);
|
||||
int cm_score(var_t v, rational const& new_value);
|
||||
void update(var_t v, rational const& new_value);
|
||||
void paws();
|
||||
rational dscore(var_t v, rational const& new_value) const;
|
||||
void save_best_values();
|
||||
void add_vars();
|
||||
sls::ineq& new_ineq(ineq_kind op, rational const& bound);
|
||||
void add_arg(sat::literal lit, ineq& ineq, rational const& c, var_t v);
|
||||
void add_bounds(sat::literal_vector& bounds);
|
||||
void add_args(ineq& ineq, lp::tv t, euf::theory_var v, rational sign);
|
||||
void init_literal(sat::literal lit);
|
||||
void set_literal(sat::literal lit, ineq& ineq);
|
||||
|
||||
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(euf::enode* n);
|
||||
void set(sat::ddfw* d);
|
||||
};
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ Author:
|
|||
#include "math/polynomial/algebraic_numbers.h"
|
||||
#include "math/polynomial/polynomial.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/smt/arith_sls.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
|
||||
namespace euf {
|
||||
|
@ -98,6 +99,7 @@ namespace arith {
|
|||
class solver : public euf::th_euf_solver {
|
||||
|
||||
friend struct arith_proof_hint;
|
||||
friend class sls;
|
||||
|
||||
struct scope {
|
||||
unsigned m_bounds_lim;
|
||||
|
@ -190,116 +192,7 @@ namespace arith {
|
|||
coeffs().pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
// 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 config {
|
||||
double cb = 0.0;
|
||||
unsigned L = 20;
|
||||
unsigned t = 45;
|
||||
unsigned max_no_improve = 500000;
|
||||
double sp = 0.0003;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct clause {
|
||||
unsigned m_weight = 1;
|
||||
rational m_dts = rational::one();
|
||||
};
|
||||
|
||||
solver& s;
|
||||
ast_manager& m;
|
||||
sat::ddfw* m_bool_search = nullptr;
|
||||
unsigned m_max_arith_steps = 0;
|
||||
unsigned m_best_min_unsat = UINT_MAX;
|
||||
stats m_stats;
|
||||
config m_config;
|
||||
scoped_ptr_vector<atom_info> m_atoms;
|
||||
vector<var_info> m_vars;
|
||||
vector<clause> m_clauses;
|
||||
|
||||
indexed_uint_set& unsat() { return m_bool_search->unsat_set(); }
|
||||
unsigned num_clauses() const { return m_bool_search->num_clauses(); }
|
||||
sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; }
|
||||
sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; }
|
||||
sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); }
|
||||
sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); }
|
||||
|
||||
atom_info* atom(sat::literal lit) const { return m_atoms[lit.index()]; }
|
||||
rational& dts(unsigned idx) { return m_clauses[idx].m_dts; }
|
||||
bool flip();
|
||||
void log() {}
|
||||
bool flip_unsat();
|
||||
bool flip_clauses();
|
||||
bool flip_dscore();
|
||||
bool flip_dscore(unsigned cl);
|
||||
bool flip(unsigned cl);
|
||||
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(unsigned cl, var_t v, rational const& new_value) const;
|
||||
rational dts(unsigned cl) const;
|
||||
bool cm(ineq const& ineq, var_t v, rational& new_value);
|
||||
int cm_score(var_t v, rational const& new_value);
|
||||
void update(var_t v, rational const& new_value);
|
||||
void paws();
|
||||
rational dscore(var_t v, rational const& new_value) const;
|
||||
void save_best_values() {}
|
||||
|
||||
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);
|
||||
void set(sat::ddfw* d) { m_bool_search = d; }
|
||||
};
|
||||
|
||||
|
||||
sls m_local_search;
|
||||
|
||||
typedef vector<std::pair<rational, lpvar>> var_coeffs;
|
||||
|
|
|
@ -24,7 +24,7 @@ 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.reinit(s(), phase);
|
||||
bool_search.updt_params(s().params());
|
||||
bool_search.set_seed(rand());
|
||||
scoped_rl.push_child(&(bool_search.rlimit()));
|
||||
|
@ -36,29 +36,29 @@ namespace euf {
|
|||
|
||||
for (unsigned rounds = 0; m.inc() && rounds < max_rounds; ++rounds) {
|
||||
|
||||
bool_search.reinit(s(), phase);
|
||||
|
||||
setup_bounds(phase);
|
||||
|
||||
// Non-boolean literals are assumptions to Boolean search
|
||||
literal_vector assumptions;
|
||||
for (unsigned v = 0; v < phase.size(); ++v)
|
||||
if (!is_propositional(literal(v)))
|
||||
assumptions.push_back(literal(v, !phase[v]));
|
||||
assumptions.push_back(literal(v, !bool_search.get_value(v)));
|
||||
|
||||
bool_search.rlimit().push(m_max_bool_steps);
|
||||
|
||||
lbool r = bool_search.check(assumptions.size(), assumptions.data(), nullptr);
|
||||
|
||||
|
||||
auto const& mdl = bool_search.get_model();
|
||||
for (unsigned i = 0; i < mdl.size(); ++i)
|
||||
phase[i] = mdl[i] == l_true;
|
||||
bool_search.rlimit().pop();
|
||||
|
||||
for (auto* th : m_solvers)
|
||||
th->local_search(phase);
|
||||
// if is_sat break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
auto const& mdl = bool_search.get_model();
|
||||
for (unsigned i = 0; i < mdl.size(); ++i)
|
||||
phase[i] = mdl[i] == l_true;
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue