mirror of
https://github.com/Z3Prover/z3
synced 2025-08-12 06:00:53 +00:00
Merge branch 'master' into polysat
This commit is contained in:
commit
f54f33551e
308 changed files with 6606 additions and 18485 deletions
|
@ -200,7 +200,6 @@ namespace sat {
|
|||
m_drat_check_sat = p.drat_check_sat();
|
||||
m_drat_file = p.drat_file();
|
||||
m_smt_proof_check = p.smt_proof_check();
|
||||
m_smt_proof_check_rup = p.smt_proof_check_rup();
|
||||
m_drat_disable = p.drat_disable();
|
||||
m_drat =
|
||||
!m_drat_disable && p.threads() == 1 &&
|
||||
|
|
|
@ -180,7 +180,6 @@ namespace sat {
|
|||
bool m_drat_binary;
|
||||
symbol m_drat_file;
|
||||
bool m_smt_proof_check;
|
||||
bool m_smt_proof_check_rup;
|
||||
bool m_drat_check_unsat;
|
||||
bool m_drat_check_sat;
|
||||
bool m_drat_activity;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
DDFW Local search module for clauses
|
||||
|
||||
Author:
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner, Marijn Heule 2019-4-23
|
||||
|
||||
|
@ -33,35 +33,58 @@
|
|||
namespace sat {
|
||||
|
||||
ddfw::~ddfw() {
|
||||
for (auto& ci : m_clauses) {
|
||||
m_alloc.del_clause(ci.m_clause);
|
||||
}
|
||||
for (auto& ci : m_clauses)
|
||||
m_alloc.del_clause(ci.m_clause);
|
||||
}
|
||||
|
||||
|
||||
lbool ddfw::check(unsigned sz, literal const* assumptions, parallel* p) {
|
||||
init(sz, assumptions);
|
||||
flet<parallel*> _p(m_par, p);
|
||||
while (m_limit.inc() && m_min_sz > 0) {
|
||||
if (should_reinit_weights()) do_reinit_weights();
|
||||
else if (do_flip()) ;
|
||||
else if (should_restart()) do_restart();
|
||||
else if (should_parallel_sync()) do_parallel_sync();
|
||||
else shift_weights();
|
||||
}
|
||||
if (m_plugin)
|
||||
check_with_plugin();
|
||||
else
|
||||
check_without_plugin();
|
||||
remove_assumptions();
|
||||
log();
|
||||
return m_min_sz == 0 ? l_true : l_undef;
|
||||
}
|
||||
|
||||
void ddfw::check_without_plugin() {
|
||||
while (m_limit.inc() && m_min_sz > 0) {
|
||||
if (should_reinit_weights()) do_reinit_weights();
|
||||
else if (do_flip<false>());
|
||||
else if (should_restart()) do_restart();
|
||||
else if (should_parallel_sync()) do_parallel_sync();
|
||||
else shift_weights();
|
||||
}
|
||||
}
|
||||
|
||||
void ddfw::check_with_plugin() {
|
||||
m_plugin->init_search();
|
||||
m_steps_since_progress = 0;
|
||||
unsigned steps = 0;
|
||||
while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) {
|
||||
if (should_reinit_weights()) do_reinit_weights();
|
||||
else if (steps % 5000 == 0) shift_weights(), m_plugin->on_rescale();
|
||||
else if (should_restart()) do_restart(), m_plugin->on_restart();
|
||||
else if (do_flip<true>());
|
||||
else if (do_literal_flip<true>());
|
||||
else if (should_parallel_sync()) do_parallel_sync();
|
||||
else shift_weights(), m_plugin->on_rescale();
|
||||
++steps;
|
||||
}
|
||||
m_plugin->finish_search();
|
||||
}
|
||||
|
||||
void ddfw::log() {
|
||||
double sec = m_stopwatch.get_current_seconds();
|
||||
double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec);
|
||||
if (m_last_flips == 0) {
|
||||
IF_VERBOSE(0, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts";
|
||||
IF_VERBOSE(1, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts";
|
||||
if (m_par) verbose_stream() << " :par";
|
||||
verbose_stream() << ")\n");
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "(sat.ddfw "
|
||||
IF_VERBOSE(1, verbose_stream() << "(sat.ddfw "
|
||||
<< std::setw(07) << m_min_sz
|
||||
<< std::setw(07) << m_models.size()
|
||||
<< std::setw(10) << kflips_per_sec
|
||||
|
@ -76,60 +99,98 @@ namespace sat {
|
|||
m_last_flips = m_flips;
|
||||
}
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool ddfw::do_flip() {
|
||||
bool_var v = pick_var();
|
||||
if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) {
|
||||
flip(v);
|
||||
if (m_unsat.size() <= m_min_sz) save_best_values();
|
||||
double reward = 0;
|
||||
bool_var v = pick_var<uses_plugin>(reward);
|
||||
return apply_flip<uses_plugin>(v, reward);
|
||||
}
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool ddfw::apply_flip(bool_var v, double reward) {
|
||||
if (v == null_bool_var)
|
||||
return false;
|
||||
|
||||
if (reward > 0 || (reward == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) {
|
||||
if (uses_plugin && is_external(v))
|
||||
m_plugin->flip(v);
|
||||
else
|
||||
flip(v);
|
||||
if (m_unsat.size() <= m_min_sz)
|
||||
save_best_values();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool_var ddfw::pick_var() {
|
||||
template<bool uses_plugin>
|
||||
bool_var ddfw::pick_var(double& r) {
|
||||
double sum_pos = 0;
|
||||
unsigned n = 1;
|
||||
bool_var v0 = null_bool_var;
|
||||
for (bool_var v : m_unsat_vars) {
|
||||
double r = reward(v);
|
||||
if (r > 0.0) {
|
||||
sum_pos += score(r);
|
||||
}
|
||||
else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) {
|
||||
v0 = v;
|
||||
}
|
||||
r = uses_plugin ? plugin_reward(v) : reward(v);
|
||||
if (r > 0.0)
|
||||
sum_pos += score(r);
|
||||
else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0)
|
||||
v0 = v;
|
||||
}
|
||||
if (sum_pos > 0) {
|
||||
double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos;
|
||||
for (bool_var v : m_unsat_vars) {
|
||||
double r = reward(v);
|
||||
r = uses_plugin && is_external(v) ? m_vars[v].m_last_reward : reward(v);
|
||||
if (r > 0) {
|
||||
lim_pos -= score(r);
|
||||
if (lim_pos <= 0) {
|
||||
if (m_par) update_reward_avg(v);
|
||||
return v;
|
||||
}
|
||||
if (lim_pos <= 0)
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (v0 != null_bool_var) {
|
||||
r = 0;
|
||||
if (v0 != null_bool_var)
|
||||
return v0;
|
||||
}
|
||||
if (m_unsat_vars.empty())
|
||||
return null_bool_var;
|
||||
return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* TBD: map reward value to a score, possibly through an exponential function, such as
|
||||
* exp(-tau/r), where tau > 0
|
||||
*/
|
||||
double ddfw::mk_score(double r) {
|
||||
return r;
|
||||
template<bool uses_plugin>
|
||||
bool ddfw::do_literal_flip() {
|
||||
double reward = 1;
|
||||
return apply_flip<uses_plugin>(pick_literal_var<uses_plugin>(), reward);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pick a random false literal from a satisfied clause such that
|
||||
* the literal has zero break count and positive reward.
|
||||
*/
|
||||
template<bool uses_plugin>
|
||||
bool_var ddfw::pick_literal_var() {
|
||||
#if false
|
||||
unsigned sz = m_clauses.size();
|
||||
unsigned start = rand();
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
unsigned cl = (i + start) % sz;
|
||||
if (m_unsat.contains(cl))
|
||||
continue;
|
||||
for (auto lit : *m_clauses[cl].m_clause) {
|
||||
if (is_true(lit))
|
||||
continue;
|
||||
double r = uses_plugin ? plugin_reward(lit.var()) : reward(lit.var());
|
||||
if (r < 0)
|
||||
continue;
|
||||
//verbose_stream() << "false " << r << " " << lit << "\n";
|
||||
return lit.var();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return null_bool_var;
|
||||
}
|
||||
|
||||
void ddfw::add(unsigned n, literal const* c) {
|
||||
clause* cls = m_alloc.mk_clause(n, c, false);
|
||||
unsigned idx = m_clauses.size();
|
||||
|
||||
m_clauses.push_back(clause_info(cls, m_config.m_init_clause_weight));
|
||||
for (literal lit : *cls) {
|
||||
m_use_list.reserve(2*(lit.var()+1));
|
||||
|
@ -138,10 +199,22 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last clause that was added
|
||||
*/
|
||||
void ddfw::del() {
|
||||
auto& info = m_clauses.back();
|
||||
for (literal lit : *info.m_clause)
|
||||
m_use_list[lit.index()].pop_back();
|
||||
m_alloc.del_clause(info.m_clause);
|
||||
m_clauses.pop_back();
|
||||
if (m_unsat.contains(m_clauses.size()))
|
||||
m_unsat.remove(m_clauses.size());
|
||||
}
|
||||
|
||||
void ddfw::add(solver const& s) {
|
||||
for (auto& ci : m_clauses) {
|
||||
for (auto& ci : m_clauses)
|
||||
m_alloc.del_clause(ci.m_clause);
|
||||
}
|
||||
m_clauses.reset();
|
||||
m_use_list.reset();
|
||||
m_num_non_binary_clauses = 0;
|
||||
|
@ -171,9 +244,16 @@ namespace sat {
|
|||
}
|
||||
|
||||
void ddfw::add_assumptions() {
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i) {
|
||||
add(1, m_assumptions.data() + i);
|
||||
}
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i)
|
||||
add(1, m_assumptions.data() + i);
|
||||
}
|
||||
|
||||
void ddfw::remove_assumptions() {
|
||||
if (m_assumptions.empty())
|
||||
return;
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i)
|
||||
del();
|
||||
init(0, nullptr);
|
||||
}
|
||||
|
||||
void ddfw::init(unsigned sz, literal const* assumptions) {
|
||||
|
@ -202,16 +282,14 @@ namespace sat {
|
|||
m_shifts = 0;
|
||||
m_stopwatch.start();
|
||||
}
|
||||
|
||||
void ddfw::reinit(solver& s) {
|
||||
|
||||
void ddfw::reinit(solver& s, bool_vector const& phase) {
|
||||
add(s);
|
||||
add_assumptions();
|
||||
if (s.m_best_phase_size > 0) {
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
value(v) = s.m_best_phase[v];
|
||||
reward(v) = 0;
|
||||
make_count(v) = 0;
|
||||
}
|
||||
for (unsigned v = 0; v < phase.size(); ++v) {
|
||||
value(v) = phase[v];
|
||||
reward(v) = 0;
|
||||
make_count(v) = 0;
|
||||
}
|
||||
init_clause_data();
|
||||
flatten_use_list();
|
||||
|
@ -227,7 +305,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));
|
||||
|
@ -281,6 +358,7 @@ namespace sat {
|
|||
ci.add(nlit);
|
||||
}
|
||||
value(v) = !value(v);
|
||||
update_reward_avg(v);
|
||||
}
|
||||
|
||||
bool ddfw::should_reinit_weights() {
|
||||
|
@ -291,19 +369,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;
|
||||
|
@ -323,11 +397,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) {
|
||||
|
@ -348,7 +420,7 @@ namespace sat {
|
|||
bool ddfw::should_restart() {
|
||||
return m_flips >= m_restart_next;
|
||||
}
|
||||
|
||||
|
||||
void ddfw::do_restart() {
|
||||
reinit_values();
|
||||
init_clause_data();
|
||||
|
@ -366,12 +438,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,36 +449,36 @@ namespace sat {
|
|||
return m_par != nullptr && m_flips >= m_parsync_next;
|
||||
}
|
||||
|
||||
void ddfw::save_priorities() {
|
||||
m_probs.reset();
|
||||
for (unsigned v = 0; v < num_vars(); ++v)
|
||||
m_probs.push_back(-m_vars[v].m_reward_avg);
|
||||
}
|
||||
|
||||
void ddfw::do_parallel_sync() {
|
||||
if (m_par->from_solver(*this)) {
|
||||
// Sum exp(xi) / exp(a) = Sum exp(xi - a)
|
||||
double max_avg = 0;
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
max_avg = std::max(max_avg, (double)m_vars[v].m_reward_avg);
|
||||
}
|
||||
double sum = 0;
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
sum += exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg));
|
||||
}
|
||||
if (sum == 0) {
|
||||
sum = 0.01;
|
||||
}
|
||||
m_probs.reset();
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
m_probs.push_back(exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)) / sum);
|
||||
}
|
||||
if (m_par->from_solver(*this))
|
||||
m_par->to_solver(*this);
|
||||
}
|
||||
|
||||
++m_parsync_count;
|
||||
m_parsync_next *= 3;
|
||||
m_parsync_next /= 2;
|
||||
}
|
||||
|
||||
void ddfw::save_model() {
|
||||
m_model.reserve(num_vars());
|
||||
for (unsigned i = 0; i < num_vars(); ++i)
|
||||
m_model[i] = to_lbool(value(i));
|
||||
save_priorities();
|
||||
if (m_plugin)
|
||||
m_plugin->on_save_model();
|
||||
}
|
||||
|
||||
|
||||
void ddfw::save_best_values() {
|
||||
if (m_unsat.empty()) {
|
||||
m_model.reserve(num_vars());
|
||||
for (unsigned i = 0; i < num_vars(); ++i)
|
||||
m_model[i] = to_lbool(value(i));
|
||||
if (m_unsat.size() < m_min_sz) {
|
||||
m_steps_since_progress = 0;
|
||||
if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11)
|
||||
save_model();
|
||||
}
|
||||
if (m_unsat.size() < m_min_sz) {
|
||||
m_models.reset();
|
||||
|
@ -420,13 +490,20 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned h = value_hash();
|
||||
unsigned occs = 0;
|
||||
bool contains = m_models.find(h, occs);
|
||||
if (!m_models.contains(h)) {
|
||||
for (unsigned v = 0; v < num_vars(); ++v)
|
||||
for (unsigned v = 0; v < num_vars(); ++v)
|
||||
bias(v) += value(v) ? 1 : -1;
|
||||
m_models.insert(h);
|
||||
if (m_models.size() > m_config.m_max_num_models)
|
||||
m_models.erase(*m_models.begin());
|
||||
if (m_models.size() > m_config.m_max_num_models)
|
||||
m_models.erase(m_models.begin()->m_key);
|
||||
}
|
||||
m_models.insert(h, occs + 1);
|
||||
if (occs > 100) {
|
||||
m_restart_next = m_flips;
|
||||
m_models.erase(h);
|
||||
}
|
||||
m_min_sz = m_unsat.size();
|
||||
}
|
||||
|
@ -514,8 +591,7 @@ namespace sat {
|
|||
void ddfw::shift_weights() {
|
||||
++m_shifts;
|
||||
for (unsigned to_idx : m_unsat) {
|
||||
auto& cf = m_clauses[to_idx];
|
||||
SASSERT(!cf.is_true());
|
||||
SASSERT(!m_clauses[to_idx].is_true());
|
||||
unsigned from_idx = select_max_same_sign(to_idx);
|
||||
if (from_idx == UINT_MAX || disregard_neighbor())
|
||||
from_idx = select_random_true_clause();
|
||||
|
|
|
@ -27,12 +27,29 @@
|
|||
#include "sat/sat_clause.h"
|
||||
#include "sat/sat_types.h"
|
||||
|
||||
namespace arith {
|
||||
class sls;
|
||||
}
|
||||
|
||||
namespace sat {
|
||||
class solver;
|
||||
class parallel;
|
||||
|
||||
class ddfw : public i_local_search {
|
||||
class local_search_plugin {
|
||||
public:
|
||||
virtual ~local_search_plugin() {}
|
||||
virtual void init_search() = 0;
|
||||
virtual void finish_search() = 0;
|
||||
virtual void flip(bool_var v) = 0;
|
||||
virtual double reward(bool_var v) = 0;
|
||||
virtual void on_rescale() = 0;
|
||||
virtual void on_save_model() = 0;
|
||||
virtual void on_restart() = 0;
|
||||
};
|
||||
|
||||
class ddfw : public i_local_search {
|
||||
friend class arith::sls;
|
||||
public:
|
||||
struct clause_info {
|
||||
clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {}
|
||||
double m_weight; // weight of clause
|
||||
|
@ -44,6 +61,19 @@ namespace sat {
|
|||
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]; }
|
||||
unsigned size() const { return p.m_use_list_index[i + 1] - p.m_use_list_index[i]; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
struct config {
|
||||
config() { reset(); }
|
||||
unsigned m_use_reward_zero_pct;
|
||||
|
@ -68,8 +98,10 @@ namespace sat {
|
|||
var_info() {}
|
||||
bool m_value = false;
|
||||
double m_reward = 0;
|
||||
double m_last_reward = 0;
|
||||
unsigned m_make_count = 0;
|
||||
int m_bias = 0;
|
||||
bool m_external = false;
|
||||
ema m_reward_avg = 1e-5;
|
||||
};
|
||||
|
||||
|
@ -95,27 +127,20 @@ namespace sat {
|
|||
unsigned m_restart_count = 0, m_reinit_count = 0, m_parsync_count = 0;
|
||||
uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0;
|
||||
uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0;
|
||||
unsigned m_min_sz = 0;
|
||||
hashtable<unsigned, unsigned_hash, default_eq<unsigned>> m_models;
|
||||
unsigned m_min_sz = 0, m_steps_since_progress = 0;
|
||||
u_map<unsigned> m_models;
|
||||
stopwatch m_stopwatch;
|
||||
|
||||
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]; }
|
||||
};
|
||||
local_search_plugin* m_plugin = nullptr;
|
||||
|
||||
void flatten_use_list();
|
||||
|
||||
double mk_score(double r);
|
||||
|
||||
inline double score(double r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; }
|
||||
/**
|
||||
* TBD: map reward value to a score, possibly through an exponential function, such as
|
||||
* exp(-tau/r), where tau > 0
|
||||
*/
|
||||
inline double score(double r) { return r; }
|
||||
|
||||
inline unsigned num_vars() const { return m_vars.size(); }
|
||||
|
||||
|
@ -129,6 +154,12 @@ namespace sat {
|
|||
|
||||
inline double reward(bool_var v) const { return m_vars[v].m_reward; }
|
||||
|
||||
inline double plugin_reward(bool_var v) { return is_external(v) ? (m_vars[v].m_last_reward = m_plugin->reward(v)) : reward(v); }
|
||||
|
||||
void set_external(bool_var v) { m_vars[v].m_external = true; }
|
||||
|
||||
inline bool is_external(bool_var v) const { return m_vars[v].m_external; }
|
||||
|
||||
inline int& bias(bool_var v) { return m_vars[v].m_bias; }
|
||||
|
||||
unsigned value_hash() const;
|
||||
|
@ -159,11 +190,28 @@ namespace sat {
|
|||
|
||||
inline void dec_reward(literal lit, double w) { reward(lit.var()) -= w; }
|
||||
|
||||
void check_with_plugin();
|
||||
void check_without_plugin();
|
||||
|
||||
// flip activity
|
||||
template<bool uses_plugin>
|
||||
bool do_flip();
|
||||
bool_var pick_var();
|
||||
void flip(bool_var v);
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool_var pick_var(double& reward);
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool apply_flip(bool_var v, double reward);
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool do_literal_flip();
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool_var pick_literal_var();
|
||||
|
||||
void save_best_values();
|
||||
void save_model();
|
||||
void save_priorities();
|
||||
|
||||
// shift activity
|
||||
void shift_weights();
|
||||
|
@ -195,6 +243,8 @@ namespace sat {
|
|||
|
||||
void add(unsigned sz, literal const* c);
|
||||
|
||||
void del();
|
||||
|
||||
void add_assumptions();
|
||||
|
||||
inline void transfer_weight(unsigned from, unsigned to, double w);
|
||||
|
@ -207,6 +257,8 @@ namespace sat {
|
|||
|
||||
~ddfw() override;
|
||||
|
||||
void set(local_search_plugin* p) { m_plugin = p; }
|
||||
|
||||
lbool check(unsigned sz, literal const* assumptions, parallel* p) override;
|
||||
|
||||
void updt_params(params_ref const& p) override;
|
||||
|
@ -225,11 +277,25 @@ namespace sat {
|
|||
|
||||
// for parallel integration
|
||||
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 {}
|
||||
|
||||
double get_priority(bool_var v) const override { return m_probs[v]; }
|
||||
|
||||
// access clause information and state of Boolean search
|
||||
indexed_uint_set& unsat_set() { return m_unsat; }
|
||||
|
||||
unsigned num_clauses() const { return m_clauses.size(); }
|
||||
|
||||
clause_info& get_clause_info(unsigned idx) { return m_clauses[idx]; }
|
||||
|
||||
void remove_assumptions();
|
||||
|
||||
void flip(bool_var v);
|
||||
|
||||
use_list get_use_list(literal lit) { return use_list(*this, lit); }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace sat {
|
|||
virtual void add_assumptions(literal_set& ext_assumptions) {}
|
||||
virtual bool tracking_assumptions() { return false; }
|
||||
virtual bool enable_self_propagate() const { return false; }
|
||||
virtual lbool local_search(bool_vector& phase) { return l_undef; }
|
||||
|
||||
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) {
|
||||
|
|
|
@ -353,19 +353,13 @@ 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) {
|
||||
import(s, true);
|
||||
if (s.m_best_phase_size > 0) {
|
||||
for (unsigned i = num_vars(); i-- > 0; ) {
|
||||
set_phase(i, s.m_best_phase[i]);
|
||||
}
|
||||
}
|
||||
void local_search::reinit(solver& s, bool_vector const& phase) {
|
||||
import(s, true);
|
||||
for (unsigned i = phase.size(); i-- > 0; )
|
||||
set_phase(i, phase[i]);
|
||||
}
|
||||
|
||||
void local_search::import(solver const& s, bool _init) {
|
||||
|
@ -378,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();
|
||||
|
@ -422,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; }
|
||||
|
@ -248,7 +248,7 @@ namespace sat {
|
|||
|
||||
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
|
||||
void set_phase(bool_var v, bool f);
|
||||
|
|
|
@ -214,14 +214,17 @@ namespace sat {
|
|||
}
|
||||
|
||||
|
||||
bool parallel::_to_solver(solver& s) {
|
||||
if (m_priorities.empty()) {
|
||||
return false;
|
||||
}
|
||||
void parallel::_to_solver(solver& s) {
|
||||
return;
|
||||
#if 0
|
||||
if (m_priorities.empty())
|
||||
return;
|
||||
|
||||
for (bool_var v = 0; v < m_priorities.size(); ++v) {
|
||||
s.update_activity(v, m_priorities[v]);
|
||||
}
|
||||
return true;
|
||||
s.m_activity_inc = 128;
|
||||
#endif
|
||||
}
|
||||
|
||||
void parallel::from_solver(solver& s) {
|
||||
|
@ -229,16 +232,19 @@ namespace sat {
|
|||
_from_solver(s);
|
||||
}
|
||||
|
||||
bool parallel::to_solver(solver& s) {
|
||||
void parallel::to_solver(solver& s) {
|
||||
lock_guard lock(m_mux);
|
||||
return _to_solver(s);
|
||||
_to_solver(s);
|
||||
}
|
||||
|
||||
void parallel::_to_solver(i_local_search& s) {
|
||||
return;
|
||||
#if 0
|
||||
m_priorities.reset();
|
||||
for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) {
|
||||
m_priorities.push_back(s.get_priority(v));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool parallel::_from_solver(i_local_search& s) {
|
||||
|
@ -246,7 +252,7 @@ namespace sat {
|
|||
m_consumer_ready = true;
|
||||
if (m_solver_copy) {
|
||||
copied = true;
|
||||
s.reinit(*m_solver_copy.get());
|
||||
s.reinit(*m_solver_copy.get(), m_solver_copy->m_best_phase);
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace sat {
|
|||
bool enable_add(clause const& c) const;
|
||||
void _get_clauses(solver& s);
|
||||
void _from_solver(solver& s);
|
||||
bool _to_solver(solver& s);
|
||||
void _to_solver(solver& s);
|
||||
bool _from_solver(i_local_search& s);
|
||||
void _to_solver(i_local_search& s);
|
||||
|
||||
|
@ -102,7 +102,7 @@ namespace sat {
|
|||
|
||||
// exchange from solver state to local search and back.
|
||||
void from_solver(solver& s);
|
||||
bool to_solver(solver& s);
|
||||
void to_solver(solver& s);
|
||||
|
||||
bool from_solver(i_local_search& s);
|
||||
void to_solver(i_local_search& s);
|
||||
|
|
|
@ -2,7 +2,7 @@ def_module_params('sat',
|
|||
export=True,
|
||||
description='propositional SAT solver',
|
||||
params=(max_memory_param(),
|
||||
('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching'),
|
||||
('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching, local_search'),
|
||||
('phase.sticky', BOOL, True, 'use sticky phase caching'),
|
||||
('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'),
|
||||
('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'),
|
||||
|
@ -48,8 +48,7 @@ def_module_params('sat',
|
|||
('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'),
|
||||
('drat.disable', BOOL, False, 'override anything that enables DRAT'),
|
||||
('smt', BOOL, False, 'use the SAT solver based incremental SMT core'),
|
||||
('smt.proof.check', BOOL, False, 'check SMT proof while it is created'),
|
||||
('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'),
|
||||
('smt.proof.check', BOOL, False, 'check proofs on the fly during SMT search'),
|
||||
('drat.file', SYMBOL, '', 'file to dump DRAT proofs'),
|
||||
('drat.binary', BOOL, False, 'use Binary DRAT output format'),
|
||||
('drat.check_unsat', BOOL, False, 'build up internal proof and check'),
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace sat {
|
|||
clause_vector m_clause_db;
|
||||
svector<clause_info> m_clauses;
|
||||
bool_vector m_values, m_best_values;
|
||||
unsigned m_best_min_unsat{ 0 };
|
||||
unsigned m_best_min_unsat = 0;
|
||||
vector<unsigned_vector> m_use_list;
|
||||
unsigned_vector m_flat_use_list;
|
||||
unsigned_vector m_use_list_index;
|
||||
|
@ -67,9 +67,9 @@ namespace sat {
|
|||
indexed_uint_set m_unsat;
|
||||
random_gen m_rand;
|
||||
unsigned_vector m_breaks;
|
||||
uint64_t m_flips{ 0 };
|
||||
uint64_t m_next_restart{ 0 };
|
||||
unsigned m_restart_count{ 0 };
|
||||
uint64_t m_flips = 0;
|
||||
uint64_t m_next_restart = 0;
|
||||
unsigned m_restart_count = 0;
|
||||
stopwatch m_stopwatch;
|
||||
model m_model;
|
||||
|
||||
|
@ -139,6 +139,8 @@ namespace sat {
|
|||
void add(solver const& s) override;
|
||||
|
||||
model const& get_model() const override { return m_model; }
|
||||
|
||||
double get_priority(bool_var v) const override { return 0; }
|
||||
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
|
||||
|
@ -148,7 +150,7 @@ namespace sat {
|
|||
|
||||
void collect_statistics(statistics& st) const override {}
|
||||
|
||||
void reinit(solver& s) override { UNREACHABLE(); }
|
||||
void reinit(solver& s, bool_vector const& phase) override { UNREACHABLE(); }
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1283,6 +1283,11 @@ namespace sat {
|
|||
return l_undef;
|
||||
}
|
||||
|
||||
if (m_config.m_phase == PS_LOCAL_SEARCH && m_ext) {
|
||||
bounded_local_search();
|
||||
// exit(0);
|
||||
}
|
||||
|
||||
log_stats();
|
||||
if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) {
|
||||
m_restart_threshold = m_config.m_burst_search;
|
||||
|
@ -1341,6 +1346,18 @@ namespace sat {
|
|||
};
|
||||
|
||||
void solver::bounded_local_search() {
|
||||
if (m_ext) {
|
||||
IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n");
|
||||
do_restart(true);
|
||||
lbool r = m_ext->local_search(m_best_phase);
|
||||
verbose_stream() << r << "\n";
|
||||
if (r == l_true) {
|
||||
m_conflicts_since_restart = 0;
|
||||
m_conflicts_since_gc = 0;
|
||||
m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
literal_vector _lits;
|
||||
scoped_limits scoped_rl(rlimit());
|
||||
m_local_search = alloc(ddfw);
|
||||
|
@ -1350,11 +1367,44 @@ namespace sat {
|
|||
m_local_search->updt_params(m_params);
|
||||
m_local_search->set_seed(m_rand());
|
||||
scoped_rl.push_child(&(m_local_search->rlimit()));
|
||||
m_local_search->rlimit().push(500000);
|
||||
m_local_search->reinit(*this);
|
||||
m_local_search->check(_lits.size(), _lits.data(), nullptr);
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i)
|
||||
m_best_phase[i] = m_local_search->get_value(i);
|
||||
|
||||
m_local_search_lim.inc(num_clauses());
|
||||
m_local_search->rlimit().push(m_local_search_lim.limit);
|
||||
|
||||
m_local_search->reinit(*this, m_best_phase);
|
||||
lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr);
|
||||
auto const& mdl = m_local_search->get_model();
|
||||
if (mdl.size() == m_best_phase.size()) {
|
||||
for (unsigned i = 0; i < m_best_phase.size(); ++i)
|
||||
m_best_phase[i] = l_true == mdl[i];
|
||||
|
||||
if (r == l_true) {
|
||||
m_conflicts_since_restart = 0;
|
||||
m_conflicts_since_gc = 0;
|
||||
m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1);
|
||||
}
|
||||
do_restart(true);
|
||||
#if 0
|
||||
// move higher priority variables to front
|
||||
// eg., move the first 10% variables to front
|
||||
svector<std::pair<double, bool_var>> priorities(mdl.size());
|
||||
for (unsigned i = 0; i < mdl.size(); ++i)
|
||||
priorities[i] = { m_local_search->get_priority(i), i };
|
||||
std::sort(priorities.begin(), priorities.end(), [](auto& x, auto& y) { return x.first > y.first; });
|
||||
for (unsigned i = priorities.size() / 10; i-- > 0; )
|
||||
move_to_front(priorities[i].second);
|
||||
#endif
|
||||
|
||||
|
||||
if (l_true == r) {
|
||||
for (clause const* cp : m_clauses) {
|
||||
bool is_true = any_of(*cp, [&](auto lit) { return lit.sign() != m_best_phase[lit.var()]; });
|
||||
if (!is_true) {
|
||||
verbose_stream() << "clause is false " << *cp << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1670,6 +1720,8 @@ namespace sat {
|
|||
push();
|
||||
m_stats.m_decision++;
|
||||
|
||||
CTRACE("sat", m_best_phase[next] != guess(next), tout << "phase " << phase << " " << m_best_phase[next] << " " << guess(next) << "\n");
|
||||
|
||||
if (phase == l_undef)
|
||||
phase = guess(next) ? l_true: l_false;
|
||||
|
||||
|
@ -1680,15 +1732,15 @@ namespace sat {
|
|||
m_case_split_queue.unassign_var_eh(next);
|
||||
next_lit = literal(next, false);
|
||||
}
|
||||
|
||||
|
||||
if (phase == l_undef)
|
||||
is_pos = guess(next);
|
||||
else
|
||||
is_pos = phase == l_true;
|
||||
|
||||
|
||||
if (!is_pos)
|
||||
next_lit.neg();
|
||||
|
||||
|
||||
TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";);
|
||||
assign_scoped(next_lit);
|
||||
return true;
|
||||
|
@ -1905,10 +1957,13 @@ namespace sat {
|
|||
m_search_sat_conflicts = m_config.m_search_sat_conflicts;
|
||||
m_search_next_toggle = m_search_unsat_conflicts;
|
||||
m_best_phase_size = 0;
|
||||
|
||||
m_reorder.lo = m_config.m_reorder_base;
|
||||
m_rephase.base = m_config.m_rephase_base;
|
||||
m_rephase_lim = 0;
|
||||
m_rephase_inc = 0;
|
||||
m_reorder_lim = m_config.m_reorder_base;
|
||||
m_reorder_inc = 0;
|
||||
m_local_search_lim.base = 500;
|
||||
|
||||
m_conflicts_since_restart = 0;
|
||||
m_force_conflict_analysis = false;
|
||||
m_restart_threshold = m_config.m_restart_initial;
|
||||
|
@ -1923,6 +1978,7 @@ namespace sat {
|
|||
m_next_simplify = m_config.m_simplify_delay;
|
||||
m_min_d_tk = 1.0;
|
||||
m_search_lvl = 0;
|
||||
|
||||
if (m_learned.size() <= 2*m_clauses.size())
|
||||
m_conflicts_since_gc = 0;
|
||||
m_restart_next_out = 0;
|
||||
|
@ -2025,9 +2081,7 @@ namespace sat {
|
|||
|
||||
if (m_par) {
|
||||
m_par->from_solver(*this);
|
||||
if (m_par->to_solver(*this)) {
|
||||
m_activity_inc = 128;
|
||||
}
|
||||
m_par->to_solver(*this);
|
||||
}
|
||||
|
||||
if (m_config.m_binspr && !inconsistent()) {
|
||||
|
@ -2909,6 +2963,7 @@ namespace sat {
|
|||
|
||||
bool solver::should_rephase() {
|
||||
return m_conflicts_since_init > m_rephase_lim;
|
||||
// return m_rephase.should_apply(m_conflicts_since_init);
|
||||
}
|
||||
|
||||
void solver::do_rephase() {
|
||||
|
@ -2922,7 +2977,7 @@ namespace sat {
|
|||
case PS_FROZEN:
|
||||
break;
|
||||
case PS_BASIC_CACHING:
|
||||
switch (m_rephase_lim % 4) {
|
||||
switch (m_rephase.count % 4) {
|
||||
case 0:
|
||||
for (auto& p : m_phase) p = (m_rand() % 2) == 0;
|
||||
break;
|
||||
|
@ -2939,14 +2994,19 @@ namespace sat {
|
|||
case PS_SAT_CACHING:
|
||||
if (m_search_state == s_sat)
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i)
|
||||
m_phase[i] = m_best_phase[i];
|
||||
m_phase[i] = m_best_phase[i];
|
||||
break;
|
||||
case PS_RANDOM:
|
||||
for (auto& p : m_phase) p = (m_rand() % 2) == 0;
|
||||
break;
|
||||
case PS_LOCAL_SEARCH:
|
||||
if (m_search_state == s_sat)
|
||||
bounded_local_search();
|
||||
if (m_search_state == s_sat) {
|
||||
if (m_rand() % 2 == 0)
|
||||
bounded_local_search();
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i)
|
||||
m_phase[i] = m_best_phase[i];
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -2954,10 +3014,11 @@ namespace sat {
|
|||
}
|
||||
m_rephase_inc += m_config.m_rephase_base;
|
||||
m_rephase_lim += m_rephase_inc;
|
||||
m_rephase.inc(m_conflicts_since_init, num_clauses());
|
||||
}
|
||||
|
||||
bool solver::should_reorder() {
|
||||
return m_conflicts_since_init > m_reorder_lim;
|
||||
return m_reorder.should_apply(m_conflicts_since_init);
|
||||
}
|
||||
|
||||
void solver::do_reorder() {
|
||||
|
@ -3001,8 +3062,7 @@ namespace sat {
|
|||
update_activity(v, m_rand(10)/10.0);
|
||||
}
|
||||
#endif
|
||||
m_reorder_inc += m_config.m_reorder_base;
|
||||
m_reorder_lim += m_reorder_inc;
|
||||
m_reorder.inc(m_conflicts_since_init, num_clauses());
|
||||
}
|
||||
|
||||
void solver::updt_phase_counters() {
|
||||
|
|
|
@ -159,10 +159,11 @@ namespace sat {
|
|||
unsigned m_search_next_toggle;
|
||||
unsigned m_phase_counter;
|
||||
unsigned m_best_phase_size;
|
||||
backoff m_local_search_lim;
|
||||
unsigned m_rephase_lim;
|
||||
unsigned m_rephase_inc;
|
||||
unsigned m_reorder_lim;
|
||||
unsigned m_reorder_inc;
|
||||
backoff m_rephase;
|
||||
backoff m_reorder;
|
||||
var_queue m_case_split_queue;
|
||||
unsigned m_qhead;
|
||||
unsigned m_scope_lvl;
|
||||
|
@ -237,6 +238,7 @@ namespace sat {
|
|||
friend class lut_finder;
|
||||
friend class npn3_finder;
|
||||
friend class proof_trim;
|
||||
friend struct backoff;
|
||||
public:
|
||||
solver(params_ref const & p, reslimit& l);
|
||||
~solver() override;
|
||||
|
@ -332,6 +334,7 @@ namespace sat {
|
|||
s.m_checkpoint_enabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
unsigned select_watch_lit(clause const & cls, unsigned starting_at) const;
|
||||
unsigned select_learned_watch_lit(clause const & cls) const;
|
||||
bool simplify_clause(unsigned & num_lits, literal * lits) const;
|
||||
|
|
|
@ -701,6 +701,10 @@ public:
|
|||
ensure_euf()->user_propagate_register_created(r);
|
||||
}
|
||||
|
||||
void user_propagate_register_decide(user_propagator::decide_eh_t& r) override {
|
||||
ensure_euf()->user_propagate_register_decide(r);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
|
90
src/sat/sat_solver/sat_smt_setup.h
Normal file
90
src/sat/sat_solver/sat_smt_setup.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_smt_setup.h
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2023-01-17
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "sat/sat_config.h"
|
||||
#include "ast/simplifiers/dependent_expr_state.h"
|
||||
|
||||
struct static_features;
|
||||
|
||||
namespace sat_smt {
|
||||
|
||||
void setup_sat_config(smt_params const& p, sat::config& config);
|
||||
|
||||
class setup {
|
||||
ast_manager& m;
|
||||
dependent_expr_state& m_st;
|
||||
smt_params& m_params;
|
||||
symbol m_logic;
|
||||
bool m_already_configured = false;
|
||||
|
||||
void setup_auto_config();
|
||||
void setup_default();
|
||||
//
|
||||
// setup_<logic>() methods do not depend on static features of the formula. So, they are safe to use
|
||||
// even in an incremental setting.
|
||||
//
|
||||
// setup_<logic>(static_features & st) can only be used if the logical context will perform a single
|
||||
// check.
|
||||
//
|
||||
void setup_QF_DT();
|
||||
void setup_QF_UF();
|
||||
void setup_QF_UF(static_features const & st);
|
||||
void setup_QF_RDL();
|
||||
void setup_QF_RDL(static_features & st);
|
||||
void setup_QF_IDL();
|
||||
void setup_QF_IDL(static_features & st);
|
||||
void setup_QF_UFIDL();
|
||||
void setup_QF_UFIDL(static_features & st);
|
||||
void setup_QF_LRA();
|
||||
void setup_QF_LRA(static_features const & st);
|
||||
void setup_QF_LIA();
|
||||
void setup_QF_LIRA(static_features const& st);
|
||||
void setup_QF_LIA(static_features const & st);
|
||||
void setup_QF_UFLIA();
|
||||
void setup_QF_UFLIA(static_features & st);
|
||||
void setup_QF_UFLRA();
|
||||
void setup_QF_BV();
|
||||
void setup_QF_AUFBV();
|
||||
void setup_QF_AX();
|
||||
void setup_QF_AX(static_features const & st);
|
||||
void setup_QF_AUFLIA();
|
||||
void setup_QF_AUFLIA(static_features const & st);
|
||||
void setup_QF_FP();
|
||||
void setup_QF_FPBV();
|
||||
void setup_QF_S();
|
||||
void setup_LRA();
|
||||
void setup_CSP();
|
||||
void setup_AUFLIA(bool simple_array = true);
|
||||
void setup_AUFLIA(static_features const & st);
|
||||
void setup_AUFLIRA(bool simple_array = true);
|
||||
void setup_UFNIA();
|
||||
void setup_UFLRA();
|
||||
void setup_AUFLIAp();
|
||||
void setup_AUFNIRA();
|
||||
void setup_QF_BVRE();
|
||||
void setup_unknown();
|
||||
void setup_unknown(static_features & st);
|
||||
|
||||
public:
|
||||
setup(ast_manager& m, dependent_expr_state& st, smt_params & params);
|
||||
void setk_already_configured() { m_already_configured = true; }
|
||||
bool already_configured() const { return m_already_configured; }
|
||||
symbol const & get_logic() const { return m_logic; }
|
||||
void operator()();
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -568,6 +568,10 @@ public:
|
|||
ensure_euf()->user_propagate_register_created(r);
|
||||
}
|
||||
|
||||
void user_propagate_register_decide(user_propagator::decide_eh_t& r) override {
|
||||
ensure_euf()->user_propagate_register_decide(r);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void add_assumption(expr* a) {
|
||||
|
|
|
@ -85,12 +85,12 @@ namespace sat {
|
|||
virtual void updt_params(params_ref const& p) = 0;
|
||||
virtual void set_seed(unsigned s) = 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 reslimit& rlimit() = 0;
|
||||
virtual model const& get_model() const = 0;
|
||||
virtual void collect_statistics(statistics& st) const = 0;
|
||||
virtual double get_priority(bool_var v) const { return 0; }
|
||||
virtual double get_priority(bool_var v) const = 0;
|
||||
virtual bool get_value(bool_var v) const { return true; }
|
||||
};
|
||||
|
||||
|
@ -136,6 +136,40 @@ namespace sat {
|
|||
std::ostream& operator<<(std::ostream& out, sat::status const& st);
|
||||
std::ostream& operator<<(std::ostream& out, sat::status_pp const& p);
|
||||
|
||||
/**
|
||||
* Special cases of kissat style general backoff calculation.
|
||||
* The version here calculates
|
||||
* limit := value*log(C)^2*n*log(n)
|
||||
* (effort calculation in kissat is based on ticks not clauses)
|
||||
*
|
||||
* respectively
|
||||
* limit := conflicts + value*log(C)^2*n*log(n)
|
||||
*/
|
||||
struct backoff {
|
||||
unsigned base = 1;
|
||||
unsigned lo = 0;
|
||||
unsigned hi = UINT_MAX;
|
||||
unsigned limit = 0;
|
||||
unsigned count = 0;
|
||||
|
||||
bool should_apply(unsigned n) const {
|
||||
return limit <= n && lo <= n && n <= hi;
|
||||
}
|
||||
|
||||
void inc(unsigned num_clauses) {
|
||||
count++;
|
||||
unsigned d = base * count * log2(count + 1);
|
||||
unsigned cl = log2(num_clauses + 2);
|
||||
limit = cl * cl * d;
|
||||
}
|
||||
|
||||
void inc(unsigned num_conflicts, unsigned num_clauses) {
|
||||
inc(num_clauses);
|
||||
limit += num_conflicts;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ z3_add_component(sat_smt
|
|||
arith_axioms.cpp
|
||||
arith_diagnostics.cpp
|
||||
arith_internalize.cpp
|
||||
arith_sls.cpp
|
||||
arith_solver.cpp
|
||||
array_axioms.cpp
|
||||
array_diagnostics.cpp
|
||||
|
@ -21,6 +22,7 @@ z3_add_component(sat_smt
|
|||
euf_ackerman.cpp
|
||||
euf_internalize.cpp
|
||||
euf_invariant.cpp
|
||||
euf_local_search.cpp
|
||||
euf_model.cpp
|
||||
euf_proof.cpp
|
||||
euf_proof_checker.cpp
|
||||
|
|
|
@ -23,6 +23,17 @@ Author:
|
|||
|
||||
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 {
|
||||
lp().display(out);
|
||||
|
|
|
@ -107,10 +107,12 @@ namespace arith {
|
|||
e = a.mk_idiv0(x, y);
|
||||
}
|
||||
else if (a.is_rem(n, x, y)) {
|
||||
e = a.mk_rem0(x, y);
|
||||
n = a.mk_rem(x, a.mk_int(0));
|
||||
e = a.mk_rem0(x, a.mk_int(0));
|
||||
}
|
||||
else if (a.is_mod(n, x, y)) {
|
||||
e = a.mk_mod0(x, y);
|
||||
n = a.mk_mod(x, a.mk_int(0));
|
||||
e = a.mk_mod0(x, a.mk_int(0));
|
||||
}
|
||||
else if (a.is_power(n, x, y)) {
|
||||
e = a.mk_power0(x, y);
|
||||
|
|
650
src/sat/smt/arith_sls.cpp
Normal file
650
src/sat/smt/arith_sls.cpp
Normal file
|
@ -0,0 +1,650 @@
|
|||
/*++
|
||||
Copyright (c) 2023 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 {
|
||||
|
||||
sls::sls(solver& s):
|
||||
s(s), m(s.m) {}
|
||||
|
||||
void sls::reset() {
|
||||
m_bool_vars.reset();
|
||||
m_vars.reset();
|
||||
m_terms.reset();
|
||||
}
|
||||
|
||||
void sls::save_best_values() {
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v)
|
||||
m_vars[v].m_best_value = m_vars[v].m_value;
|
||||
check_ineqs();
|
||||
if (unsat().size() == 1) {
|
||||
auto idx = *unsat().begin();
|
||||
verbose_stream() << idx << "\n";
|
||||
auto const& c = *m_bool_search->m_clauses[idx].m_clause;
|
||||
verbose_stream() << c << "\n";
|
||||
for (auto lit : c) {
|
||||
bool_var bv = lit.var();
|
||||
ineq* i = atom(bv);
|
||||
if (i)
|
||||
verbose_stream() << lit << ": " << *i << "\n";
|
||||
}
|
||||
verbose_stream() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void sls::store_best_values() {
|
||||
// first compute assignment to terms
|
||||
// then update non-basic variables in tableau.
|
||||
|
||||
if (!unsat().empty())
|
||||
return;
|
||||
|
||||
for (auto const& [t,v] : m_terms) {
|
||||
int64_t val = 0;
|
||||
lp::lar_term const& term = s.lp().get_term(t);
|
||||
for (lp::lar_term::ival const& arg : term) {
|
||||
auto t2 = s.lp().column2tv(arg.column());
|
||||
auto w = s.lp().local_to_external(t2.id());
|
||||
val += to_numeral(arg.coeff()) * m_vars[w].m_best_value;
|
||||
}
|
||||
if (v == 52) {
|
||||
verbose_stream() << "update v" << v << " := " << val << "\n";
|
||||
for (lp::lar_term::ival const& arg : term) {
|
||||
auto t2 = s.lp().column2tv(arg.column());
|
||||
auto w = s.lp().local_to_external(t2.id());
|
||||
verbose_stream() << "v" << w << " := " << m_vars[w].m_best_value << " * " << to_numeral(arg.coeff()) << "\n";
|
||||
}
|
||||
}
|
||||
m_vars[v].m_best_value = val;
|
||||
}
|
||||
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
if (s.is_bool(v))
|
||||
continue;
|
||||
if (!s.lp().external_is_used(v))
|
||||
continue;
|
||||
int64_t new_value = m_vars[v].m_best_value;
|
||||
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())) {
|
||||
rational new_value_(new_value, rational::i64());
|
||||
lp::impq val(new_value_, rational::zero());
|
||||
s.lp().set_value_for_nbasic_column(vj.index(), val);
|
||||
}
|
||||
}
|
||||
|
||||
lbool r = s.make_feasible();
|
||||
VERIFY (!unsat().empty() || r == l_true);
|
||||
#if 0
|
||||
if (unsat().empty())
|
||||
s.m_num_conflicts = s.get_config().m_arith_propagation_threshold;
|
||||
#endif
|
||||
|
||||
auto check_bool_var = [&](sat::bool_var bv) {
|
||||
auto* ineq = m_bool_vars.get(bv, nullptr);
|
||||
if (!ineq)
|
||||
return;
|
||||
api_bound* b = nullptr;
|
||||
s.m_bool_var2bound.find(bv, b);
|
||||
if (!b)
|
||||
return;
|
||||
auto bound = b->get_value();
|
||||
theory_var v = b->get_var();
|
||||
if (s.get_phase(bv) == m_bool_search->get_model()[bv])
|
||||
return;
|
||||
switch (b->get_bound_kind()) {
|
||||
case lp_api::lower_t:
|
||||
verbose_stream() << "v" << v << " " << bound << " <= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n";
|
||||
break;
|
||||
case lp_api::upper_t:
|
||||
verbose_stream() << "v" << v << " " << bound << " >= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n";
|
||||
break;
|
||||
}
|
||||
int64_t value = 0;
|
||||
for (auto const& [coeff, v] : ineq->m_args) {
|
||||
value += coeff * m_vars[v].m_best_value;
|
||||
}
|
||||
ineq->m_args_value = value;
|
||||
verbose_stream() << *ineq << " dtt " << dtt(false, *ineq) << " phase " << s.get_phase(bv) << " model " << m_bool_search->get_model()[bv] << "\n";
|
||||
for (auto const& [coeff, v] : ineq->m_args)
|
||||
verbose_stream() << "v" << v << " := " << m_vars[v].m_best_value << "\n";
|
||||
s.display(verbose_stream());
|
||||
display(verbose_stream());
|
||||
UNREACHABLE();
|
||||
exit(0);
|
||||
};
|
||||
|
||||
if (unsat().empty()) {
|
||||
for (bool_var v = 0; v < s.s().num_vars(); ++v)
|
||||
check_bool_var(v);
|
||||
}
|
||||
}
|
||||
|
||||
void sls::set(sat::ddfw* d) {
|
||||
m_bool_search = d;
|
||||
reset();
|
||||
m_bool_vars.reserve(s.s().num_vars());
|
||||
add_vars();
|
||||
for (unsigned i = 0; i < d->num_clauses(); ++i)
|
||||
for (sat::literal lit : *d->get_clause_info(i).m_clause)
|
||||
init_bool_var(lit.var());
|
||||
for (unsigned v = 0; v < s.s().num_vars(); ++v)
|
||||
init_bool_var_assignment(v);
|
||||
|
||||
d->set(this);
|
||||
}
|
||||
|
||||
// distance to true
|
||||
int64_t sls::dtt(bool sign, int64_t args, ineq const& ineq) const {
|
||||
switch (ineq.m_op) {
|
||||
case ineq_kind::LE:
|
||||
if (sign) {
|
||||
if (args <= ineq.m_bound)
|
||||
return ineq.m_bound - args + 1;
|
||||
return 0;
|
||||
}
|
||||
if (args <= ineq.m_bound)
|
||||
return 0;
|
||||
return args - ineq.m_bound;
|
||||
case ineq_kind::EQ:
|
||||
if (sign) {
|
||||
if (args == ineq.m_bound)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
if (args == ineq.m_bound)
|
||||
return 0;
|
||||
return 1;
|
||||
case ineq_kind::NE:
|
||||
if (sign) {
|
||||
if (args == ineq.m_bound)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
if (args == ineq.m_bound)
|
||||
return 1;
|
||||
return 0;
|
||||
case ineq_kind::LT:
|
||||
if (sign) {
|
||||
if (args < ineq.m_bound)
|
||||
return ineq.m_bound - args;
|
||||
return 0;
|
||||
}
|
||||
if (args < ineq.m_bound)
|
||||
return 0;
|
||||
return args - ineq.m_bound + 1;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// dtt is high overhead. It walks ineq.m_args
|
||||
// m_vars[w].m_value can be computed outside and shared among calls
|
||||
// different data-structures for storing coefficients
|
||||
//
|
||||
int64_t sls::dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const {
|
||||
for (auto const& [coeff, w] : ineq.m_args)
|
||||
if (w == v)
|
||||
return dtt(sign, ineq.m_args_value + coeff * (new_value - m_vars[v].m_value), ineq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int64_t sls::dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const {
|
||||
return dtt(sign, ineq.m_args_value + coeff * (new_value - old_value), ineq);
|
||||
}
|
||||
|
||||
bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t& new_value) {
|
||||
for (auto const& [coeff, w] : ineq.m_args)
|
||||
if (w == v)
|
||||
return cm(old_sign, ineq, v, coeff, new_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) {
|
||||
SASSERT(ineq.is_true() != old_sign);
|
||||
VERIFY(ineq.is_true() != old_sign);
|
||||
auto bound = ineq.m_bound;
|
||||
auto argsv = ineq.m_args_value;
|
||||
bool solved = false;
|
||||
int64_t delta = argsv - bound;
|
||||
auto make_eq = [&]() {
|
||||
SASSERT(delta != 0);
|
||||
if (delta < 0)
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
else
|
||||
new_value = value(v) - (delta + abs(coeff) - 1) / coeff;
|
||||
solved = argsv + coeff * (new_value - value(v)) == bound;
|
||||
if (!solved && abs(coeff) == 1) {
|
||||
verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n";
|
||||
verbose_stream() << new_value << " " << value(v) << " delta " << delta << " lhs " << (argsv + coeff * (new_value - value(v))) << " bound " << bound << "\n";
|
||||
UNREACHABLE();
|
||||
}
|
||||
return solved;
|
||||
};
|
||||
|
||||
auto make_diseq = [&]() {
|
||||
if (delta >= 0)
|
||||
delta++;
|
||||
else
|
||||
delta--;
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) != bound);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!old_sign) {
|
||||
switch (ineq.m_op) {
|
||||
case ineq_kind::LE:
|
||||
// args <= bound -> args > bound
|
||||
SASSERT(argsv <= bound);
|
||||
SASSERT(delta <= 0);
|
||||
--delta;
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) > bound);
|
||||
return true;
|
||||
case ineq_kind::LT:
|
||||
// args < bound -> args >= bound
|
||||
SASSERT(argsv <= ineq.m_bound);
|
||||
SASSERT(delta <= 0);
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) >= bound);
|
||||
return true;
|
||||
case ineq_kind::EQ:
|
||||
return make_diseq();
|
||||
case ineq_kind::NE:
|
||||
return make_eq();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (ineq.m_op) {
|
||||
case ineq_kind::LE:
|
||||
SASSERT(argsv > ineq.m_bound);
|
||||
SASSERT(delta > 0);
|
||||
new_value = value(v) - (delta + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) <= bound);
|
||||
return true;
|
||||
case ineq_kind::LT:
|
||||
SASSERT(argsv >= ineq.m_bound);
|
||||
SASSERT(delta >= 0);
|
||||
++delta;
|
||||
new_value = value(v) - (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) < bound);
|
||||
return true;
|
||||
case ineq_kind::NE:
|
||||
return make_diseq();
|
||||
case ineq_kind::EQ:
|
||||
return make_eq();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// flip on the first positive score
|
||||
// it could be changed to flip on maximal positive score
|
||||
// or flip on maximal non-negative score
|
||||
// or flip on first non-negative score
|
||||
bool sls::flip(bool sign, ineq const& ineq) {
|
||||
int64_t new_value;
|
||||
auto v = ineq.m_var_to_flip;
|
||||
if (v == UINT_MAX) {
|
||||
IF_VERBOSE(1, verbose_stream() << "no var to flip\n");
|
||||
return false;
|
||||
}
|
||||
if (!cm(sign, ineq, v, new_value)) {
|
||||
verbose_stream() << "no critical move for " << v << "\n";
|
||||
return false;
|
||||
}
|
||||
update(v, new_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c)
|
||||
// TODO - use cached dts instead of computed dts
|
||||
// cached dts has to be updated when the score of literals are updated.
|
||||
//
|
||||
double sls::dscore(var_t v, int64_t new_value) const {
|
||||
double score = 0;
|
||||
auto const& vi = m_vars[v];
|
||||
for (auto const& [coeff, bv] : vi.m_bool_vars) {
|
||||
sat::literal lit(bv, false);
|
||||
for (auto cl : m_bool_search->get_use_list(lit))
|
||||
score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl);
|
||||
for (auto cl : m_bool_search->get_use_list(~lit))
|
||||
score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
//
|
||||
// cm_score is costly. It involves several cache misses.
|
||||
// Note that
|
||||
// - m_bool_search->get_use_list(lit).size() is "often" 1 or 2
|
||||
// - dtt_old can be saved
|
||||
//
|
||||
int sls::cm_score(var_t v, int64_t new_value) {
|
||||
int score = 0;
|
||||
auto& vi = m_vars[v];
|
||||
int64_t old_value = vi.m_value;
|
||||
for (auto const& [coeff, bv] : vi.m_bool_vars) {
|
||||
auto const& ineq = *atom(bv);
|
||||
bool old_sign = sign(bv);
|
||||
int64_t dtt_old = dtt(old_sign, ineq);
|
||||
int64_t dtt_new = dtt(old_sign, ineq, coeff, old_value, new_value);
|
||||
if ((dtt_old == 0) == (dtt_new == 0))
|
||||
continue;
|
||||
sat::literal lit(bv, old_sign);
|
||||
if (dtt_old == 0)
|
||||
// flip from true to false
|
||||
lit.neg();
|
||||
|
||||
// lit flips form false to true:
|
||||
for (auto cl : m_bool_search->get_use_list(lit)) {
|
||||
auto const& clause = get_clause_info(cl);
|
||||
if (!clause.is_true())
|
||||
++score;
|
||||
}
|
||||
// ignore the situation where clause contains multiple literals using v
|
||||
for (auto cl : m_bool_search->get_use_list(~lit)) {
|
||||
auto const& clause = get_clause_info(cl);
|
||||
if (clause.m_num_trues == 1)
|
||||
--score;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
int64_t sls::compute_dts(unsigned cl) const {
|
||||
int64_t d(1), d2;
|
||||
bool first = true;
|
||||
for (auto a : get_clause(cl)) {
|
||||
auto const* ineq = atom(a.var());
|
||||
if (!ineq)
|
||||
continue;
|
||||
d2 = dtt(a.sign(), *ineq);
|
||||
if (first)
|
||||
d = d2, first = false;
|
||||
else
|
||||
d = std::min(d, d2);
|
||||
if (d == 0)
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int64_t sls::dts(unsigned cl, var_t v, int64_t new_value) const {
|
||||
int64_t d(1), d2;
|
||||
bool first = true;
|
||||
for (auto lit : get_clause(cl)) {
|
||||
auto const* ineq = atom(lit.var());
|
||||
if (!ineq)
|
||||
continue;
|
||||
d2 = dtt(lit.sign(), *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, int64_t new_value) {
|
||||
auto& vi = m_vars[v];
|
||||
auto old_value = vi.m_value;
|
||||
for (auto const& [coeff, bv] : vi.m_bool_vars) {
|
||||
auto& ineq = *atom(bv);
|
||||
bool old_sign = sign(bv);
|
||||
sat::literal lit(bv, old_sign);
|
||||
SASSERT(is_true(lit));
|
||||
ineq.m_args_value += coeff * (new_value - old_value);
|
||||
int64_t dtt_new = dtt(old_sign, ineq);
|
||||
if (dtt_new != 0)
|
||||
m_bool_search->flip(bv);
|
||||
SASSERT(dtt(sign(bv), ineq) == 0);
|
||||
}
|
||||
vi.m_value = new_value;
|
||||
}
|
||||
|
||||
void sls::add_vars() {
|
||||
SASSERT(m_vars.empty());
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
int64_t value = s.is_registered_var(v) ? to_numeral(s.get_ivalue(v).x) : 0;
|
||||
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, int64_t const& bound) {
|
||||
auto* i = alloc(ineq);
|
||||
i->m_bound = bound;
|
||||
i->m_op = op;
|
||||
return *i;
|
||||
}
|
||||
|
||||
void sls::add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v) {
|
||||
ineq.m_args.push_back({ c, v });
|
||||
ineq.m_args_value += c * value(v);
|
||||
m_vars[v].m_bool_vars.push_back({ c, bv});
|
||||
}
|
||||
|
||||
int64_t sls::to_numeral(rational const& r) {
|
||||
if (r.is_int64())
|
||||
return r.get_int64();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sls::add_args(sat::bool_var bv, ineq& ineq, lp::tv t, theory_var v, int64_t sign) {
|
||||
if (t.is_term()) {
|
||||
lp::lar_term const& term = s.lp().get_term(t);
|
||||
m_terms.push_back({t,v});
|
||||
for (lp::lar_term::ival arg : term) {
|
||||
auto t2 = s.lp().column2tv(arg.column());
|
||||
auto w = s.lp().local_to_external(t2.id());
|
||||
add_arg(bv, ineq, sign * to_numeral(arg.coeff()), w);
|
||||
}
|
||||
}
|
||||
else
|
||||
add_arg(bv, ineq, sign, s.lp().local_to_external(t.id()));
|
||||
}
|
||||
|
||||
void sls::init_bool_var(sat::bool_var bv) {
|
||||
if (m_bool_vars.get(bv, nullptr))
|
||||
return;
|
||||
api_bound* b = nullptr;
|
||||
s.m_bool_var2bound.find(bv, b);
|
||||
if (b) {
|
||||
auto t = b->tv();
|
||||
rational bound = b->get_value();
|
||||
bool should_minus = false;
|
||||
sls::ineq_kind op;
|
||||
should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t;
|
||||
op = sls::ineq_kind::LE;
|
||||
if (should_minus)
|
||||
bound.neg();
|
||||
|
||||
auto& ineq = new_ineq(op, to_numeral(bound));
|
||||
|
||||
|
||||
add_args(bv, ineq, t, b->get_var(), should_minus ? -1 : 1);
|
||||
m_bool_vars.set(bv, &ineq);
|
||||
m_bool_search->set_external(bv);
|
||||
return;
|
||||
}
|
||||
|
||||
expr* e = s.bool_var2expr(bv);
|
||||
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(sls::ineq_kind::EQ, 0);
|
||||
add_args(bv, ineq, tu, u, 1);
|
||||
add_args(bv, ineq, tv, v, -1);
|
||||
m_bool_vars.set(bv, &ineq);
|
||||
m_bool_search->set_external(bv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void sls::init_bool_var_assignment(sat::bool_var v) {
|
||||
auto* ineq = m_bool_vars.get(v, nullptr);
|
||||
if (ineq && is_true(sat::literal(v, false)) != (dtt(false, *ineq) == 0))
|
||||
m_bool_search->flip(v);
|
||||
}
|
||||
|
||||
void sls::init_search() {
|
||||
on_restart();
|
||||
}
|
||||
|
||||
void sls::finish_search() {
|
||||
store_best_values();
|
||||
}
|
||||
|
||||
void sls::flip(sat::bool_var v) {
|
||||
sat::literal lit(v, !sign(v));
|
||||
SASSERT(!is_true(lit));
|
||||
auto const* ineq = atom(v);
|
||||
if (!ineq)
|
||||
IF_VERBOSE(0, verbose_stream() << "no inequality for variable " << v << "\n");
|
||||
if (!ineq)
|
||||
return;
|
||||
SASSERT(ineq->is_true() == lit.sign());
|
||||
flip(sign(v), *ineq);
|
||||
}
|
||||
|
||||
double sls::reward(sat::bool_var v) {
|
||||
if (m_dscore_mode)
|
||||
return dscore_reward(v);
|
||||
else
|
||||
return dtt_reward(v);
|
||||
}
|
||||
|
||||
double sls::dtt_reward(sat::bool_var bv0) {
|
||||
bool sign0 = sign(bv0);
|
||||
auto* ineq = atom(bv0);
|
||||
if (!ineq)
|
||||
return -1;
|
||||
int64_t new_value;
|
||||
double max_result = -1;
|
||||
for (auto const & [coeff, x] : ineq->m_args) {
|
||||
if (!cm(sign0, *ineq, x, coeff, new_value))
|
||||
continue;
|
||||
double result = 0;
|
||||
auto old_value = m_vars[x].m_value;
|
||||
for (auto const& [coeff, bv] : m_vars[x].m_bool_vars) {
|
||||
result += m_bool_search->reward(bv);
|
||||
continue;
|
||||
bool old_sign = sign(bv);
|
||||
auto dtt_old = dtt(old_sign, *atom(bv));
|
||||
auto dtt_new = dtt(old_sign, *atom(bv), coeff, old_value, new_value);
|
||||
if ((dtt_new == 0) != (dtt_old == 0))
|
||||
result += m_bool_search->reward(bv);
|
||||
}
|
||||
if (result > max_result) {
|
||||
max_result = result;
|
||||
ineq->m_var_to_flip = x;
|
||||
}
|
||||
}
|
||||
return max_result;
|
||||
}
|
||||
|
||||
double sls::dscore_reward(sat::bool_var bv) {
|
||||
m_dscore_mode = false;
|
||||
bool old_sign = sign(bv);
|
||||
sat::literal litv(bv, old_sign);
|
||||
auto* ineq = atom(bv);
|
||||
if (!ineq)
|
||||
return 0;
|
||||
SASSERT(ineq->is_true() != old_sign);
|
||||
int64_t new_value;
|
||||
|
||||
for (auto const& [coeff, v] : ineq->m_args) {
|
||||
double result = 0;
|
||||
if (cm(old_sign, *ineq, v, coeff, new_value))
|
||||
result = dscore(v, new_value);
|
||||
// just pick first positive, or pick a max?
|
||||
if (result > 0) {
|
||||
ineq->m_var_to_flip = v;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// switch to dscore mode
|
||||
void sls::on_rescale() {
|
||||
m_dscore_mode = true;
|
||||
}
|
||||
|
||||
void sls::on_save_model() {
|
||||
save_best_values();
|
||||
}
|
||||
|
||||
void sls::on_restart() {
|
||||
for (unsigned v = 0; v < s.s().num_vars(); ++v)
|
||||
init_bool_var_assignment(v);
|
||||
|
||||
check_ineqs();
|
||||
}
|
||||
|
||||
void sls::check_ineqs() {
|
||||
|
||||
auto check_bool_var = [&](sat::bool_var bv) {
|
||||
auto const* ineq = atom(bv);
|
||||
if (!ineq)
|
||||
return;
|
||||
int64_t d = dtt(sign(bv), *ineq);
|
||||
sat::literal lit(bv, sign(bv));
|
||||
if (is_true(lit) != (d == 0)) {
|
||||
verbose_stream() << "invalid assignment " << bv << " " << *ineq << "\n";
|
||||
}
|
||||
VERIFY(is_true(lit) == (d == 0));
|
||||
};
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v)
|
||||
check_bool_var(v);
|
||||
}
|
||||
|
||||
std::ostream& sls::display(std::ostream& out) const {
|
||||
for (bool_var bv = 0; bv < s.s().num_vars(); ++bv) {
|
||||
auto const* ineq = atom(bv);
|
||||
if (!ineq)
|
||||
continue;
|
||||
out << bv << " " << *ineq << "\n";
|
||||
}
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
if (s.is_bool(v))
|
||||
continue;
|
||||
out << "v" << v << " := " << m_vars[v].m_value << " " << m_vars[v].m_best_value << "\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
170
src/sat/smt/arith_sls.h
Normal file
170
src/sat/smt/arith_sls.h
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*++
|
||||
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/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 : public sat::local_search_plugin {
|
||||
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;
|
||||
};
|
||||
|
||||
public:
|
||||
// encode args <= bound, args = bound, args < bound
|
||||
struct ineq {
|
||||
vector<std::pair<int64_t, var_t>> m_args;
|
||||
ineq_kind m_op = ineq_kind::LE;
|
||||
int64_t m_bound;
|
||||
int64_t m_args_value;
|
||||
unsigned m_var_to_flip = UINT_MAX;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
std::ostream& display(std::ostream& out) const {
|
||||
bool first = true;
|
||||
for (auto const& [c, v] : m_args)
|
||||
out << (first ? "" : " + ") << c << " * v" << v, first = false;
|
||||
switch (m_op) {
|
||||
case ineq_kind::LE:
|
||||
return out << " <= " << m_bound << "(" << m_args_value << ")";
|
||||
case ineq_kind::EQ:
|
||||
return out << " == " << m_bound << "(" << m_args_value << ")";
|
||||
case ineq_kind::NE:
|
||||
return out << " != " << m_bound << "(" << m_args_value << ")";
|
||||
default:
|
||||
return out << " < " << m_bound << "(" << m_args_value << ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
private:
|
||||
|
||||
struct var_info {
|
||||
int64_t m_value;
|
||||
int64_t m_best_value;
|
||||
var_kind m_kind = var_kind::INT;
|
||||
svector<std::pair<int64_t, sat::bool_var>> m_bool_vars;
|
||||
};
|
||||
|
||||
solver& s;
|
||||
ast_manager& m;
|
||||
sat::ddfw* m_bool_search = nullptr;
|
||||
stats m_stats;
|
||||
config m_config;
|
||||
scoped_ptr_vector<ineq> m_bool_vars;
|
||||
vector<var_info> m_vars;
|
||||
svector<std::pair<lp::tv, euf::theory_var>> m_terms;
|
||||
bool m_dscore_mode = false;
|
||||
|
||||
|
||||
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); }
|
||||
bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); }
|
||||
bool sign(sat::bool_var v) const { return !m_bool_search->get_value(v); }
|
||||
|
||||
void reset();
|
||||
ineq* atom(sat::bool_var bv) const { return m_bool_vars[bv]; }
|
||||
|
||||
bool flip(bool sign, ineq const& ineq);
|
||||
int64_t dtt(bool sign, ineq const& ineq) const { return dtt(sign, ineq.m_args_value, ineq); }
|
||||
int64_t dtt(bool sign, int64_t args_value, ineq const& ineq) const;
|
||||
int64_t dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const;
|
||||
int64_t dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const;
|
||||
int64_t dts(unsigned cl, var_t v, int64_t new_value) const;
|
||||
int64_t compute_dts(unsigned cl) const;
|
||||
bool cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value);
|
||||
bool cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value);
|
||||
int cm_score(var_t v, int64_t new_value);
|
||||
void update(var_t v, int64_t new_value);
|
||||
double dscore_reward(sat::bool_var v);
|
||||
double dtt_reward(sat::bool_var v);
|
||||
double dscore(var_t v, int64_t new_value) const;
|
||||
void save_best_values();
|
||||
void store_best_values();
|
||||
void add_vars();
|
||||
sls::ineq& new_ineq(ineq_kind op, int64_t const& bound);
|
||||
void add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v);
|
||||
void add_args(sat::bool_var bv, ineq& ineq, lp::tv t, euf::theory_var v, int64_t sign);
|
||||
void init_bool_var(sat::bool_var v);
|
||||
void init_bool_var_assignment(sat::bool_var v);
|
||||
|
||||
int64_t value(var_t v) const { return m_vars[v].m_value; }
|
||||
int64_t to_numeral(rational const& r);
|
||||
|
||||
void check_ineqs();
|
||||
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
|
||||
public:
|
||||
sls(solver& s);
|
||||
~sls() override {}
|
||||
void set(sat::ddfw* d);
|
||||
void init_search() override;
|
||||
void finish_search() override;
|
||||
void flip(sat::bool_var v) override;
|
||||
double reward(sat::bool_var v) override;
|
||||
void on_rescale() override;
|
||||
void on_save_model() override;
|
||||
void on_restart() override;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, sls::ineq const& ineq) {
|
||||
return ineq.display(out);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ namespace arith {
|
|||
solver::solver(euf::solver& ctx, theory_id id) :
|
||||
th_euf_solver(ctx, symbol("arith"), id),
|
||||
m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)),
|
||||
m_local_search(*this),
|
||||
m_resource_limit(*this),
|
||||
m_bp(*this),
|
||||
a(m),
|
||||
|
@ -100,8 +101,7 @@ namespace arith {
|
|||
return false;
|
||||
|
||||
switch (lbl) {
|
||||
case l_false:
|
||||
TRACE("arith", tout << "propagation conflict\n";);
|
||||
case l_false:
|
||||
get_infeasibility_explanation_and_set_conflict();
|
||||
break;
|
||||
case l_true:
|
||||
|
@ -381,9 +381,9 @@ namespace arith {
|
|||
|
||||
|
||||
void solver::assert_bound(bool is_true, api_bound& b) {
|
||||
TRACE("arith", tout << b << "\n";);
|
||||
lp::constraint_index ci = b.get_constraint(is_true);
|
||||
lp().activate(ci);
|
||||
TRACE("arith", tout << b << " " << is_infeasible() << "\n";);
|
||||
if (is_infeasible())
|
||||
return;
|
||||
lp::lconstraint_kind k = bound2constraint_kind(b.is_int(), b.get_bound_kind(), is_true);
|
||||
|
@ -1065,6 +1065,7 @@ namespace arith {
|
|||
TRACE("pcs", tout << lp().constraints(););
|
||||
auto status = lp().find_feasible_solution();
|
||||
TRACE("arith_verbose", display(tout););
|
||||
TRACE("arith", tout << status << "\n");
|
||||
switch (status) {
|
||||
case lp::lp_status::INFEASIBLE:
|
||||
return l_false;
|
||||
|
@ -1201,7 +1202,7 @@ namespace arith {
|
|||
|
||||
TRACE("arith",
|
||||
tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n";
|
||||
for (literal c : m_core) tout << literal2expr(c) << "\n";
|
||||
for (literal c : m_core) tout << c << ": " << literal2expr(c) << "\n";
|
||||
for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";);
|
||||
|
||||
if (is_conflict) {
|
||||
|
|
|
@ -19,9 +19,7 @@ Author:
|
|||
#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"
|
||||
|
@ -30,6 +28,8 @@ 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 {
|
||||
class solver;
|
||||
|
@ -78,12 +78,7 @@ namespace arith {
|
|||
m_eq_tail++;
|
||||
}
|
||||
public:
|
||||
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_type(euf::solver& ctx, hint_type ty);
|
||||
void set_num_le(unsigned n) { m_num_le = n; }
|
||||
void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); }
|
||||
void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); }
|
||||
|
@ -96,15 +91,13 @@ namespace arith {
|
|||
}
|
||||
std::pair<rational, literal> const& lit(unsigned i) const { return m_literals[i]; }
|
||||
std::tuple<enode*, enode*, bool> const& eq(unsigned i) const { return m_eqs[i]; }
|
||||
arith_proof_hint* mk(euf::solver& s) {
|
||||
return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail);
|
||||
}
|
||||
arith_proof_hint* mk(euf::solver& s);
|
||||
};
|
||||
|
||||
|
||||
class solver : public euf::th_euf_solver {
|
||||
|
||||
friend struct arith_proof_hint;
|
||||
friend class sls;
|
||||
|
||||
struct scope {
|
||||
unsigned m_bounds_lim;
|
||||
|
@ -144,7 +137,7 @@ namespace arith {
|
|||
};
|
||||
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
|
||||
|
@ -197,6 +190,8 @@ namespace arith {
|
|||
coeffs().pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
sls m_local_search;
|
||||
|
||||
typedef vector<std::pair<rational, lpvar>> var_coeffs;
|
||||
vector<rational> m_columns;
|
||||
|
@ -233,10 +228,10 @@ namespace arith {
|
|||
unsigned m_asserted_qhead = 0;
|
||||
|
||||
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;
|
||||
|
||||
unsigned m_num_conflicts{ 0 };
|
||||
unsigned m_num_conflicts = 0;
|
||||
lp_api::stats m_stats;
|
||||
svector<scope> m_scopes;
|
||||
|
||||
|
@ -515,6 +510,8 @@ namespace arith {
|
|||
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; }
|
||||
|
||||
void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); }
|
||||
|
||||
// bounds and equality propagation callbacks
|
||||
lp::lar_solver& lp() { return *m_solver; }
|
||||
lp::lar_solver const& lp() const { return *m_solver; }
|
||||
|
@ -523,4 +520,7 @@ namespace arith {
|
|||
void consume(rational const& v, lp::constraint_index j);
|
||||
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
50
src/sat/smt/euf_local_search.cpp
Normal file
50
src/sat/smt/euf_local_search.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*++
|
||||
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 {
|
||||
|
||||
lbool solver::local_search(bool_vector& phase) {
|
||||
scoped_limits scoped_rl(m.limit());
|
||||
sat::ddfw bool_search;
|
||||
bool_search.reinit(s(), phase);
|
||||
bool_search.updt_params(s().params());
|
||||
bool_search.set_seed(rand());
|
||||
scoped_rl.push_child(&(bool_search.rlimit()));
|
||||
|
||||
for (auto* th : m_solvers)
|
||||
th->set_bool_search(&bool_search);
|
||||
|
||||
bool_search.check(0, nullptr, nullptr);
|
||||
|
||||
auto const& mdl = bool_search.get_model();
|
||||
for (unsigned i = 0; i < mdl.size(); ++i)
|
||||
phase[i] = mdl[i] == l_true;
|
||||
|
||||
if (bool_search.unsat_set().empty()) {
|
||||
enable_trace("arith");
|
||||
enable_trace("sat");
|
||||
enable_trace("euf");
|
||||
TRACE("sat", s().display(tout));
|
||||
}
|
||||
|
||||
return bool_search.unsat_set().empty() ? l_true : l_undef;
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ namespace euf {
|
|||
m_qmodel = mdl;
|
||||
}
|
||||
|
||||
void solver::update_model(model_ref& mdl) {
|
||||
void solver::update_model(model_ref& mdl, bool validate) {
|
||||
TRACE("model", tout << "create model\n";);
|
||||
if (m_qmodel) {
|
||||
mdl = m_qmodel;
|
||||
|
@ -87,7 +87,8 @@ namespace euf {
|
|||
for (auto* mb : m_solvers)
|
||||
mb->finalize_model(*mdl);
|
||||
TRACE("model", tout << "created model " << *mdl << "\n";);
|
||||
validate_model(*mdl);
|
||||
if (validate)
|
||||
validate_model(*mdl);
|
||||
}
|
||||
|
||||
bool solver::include_func_interp(func_decl* f) {
|
||||
|
|
|
@ -28,7 +28,7 @@ Author:
|
|||
#include "sat/smt/bv_theory_checker.h"
|
||||
#include "sat/smt/distinct_theory_checker.h"
|
||||
#include "sat/smt/tseitin_theory_checker.h"
|
||||
|
||||
#include "params/solver_params.hpp"
|
||||
|
||||
namespace euf {
|
||||
|
||||
|
@ -388,8 +388,8 @@ namespace euf {
|
|||
m_sat_solver.updt_params(m_params);
|
||||
m_drat.updt_config();
|
||||
m_rup = symbol("rup");
|
||||
sat_params sp(m_params);
|
||||
m_check_rup = sp.smt_proof_check_rup();
|
||||
solver_params sp(m_params);
|
||||
m_check_rup = sp.proof_check_rup();
|
||||
}
|
||||
|
||||
void smt_proof_checker::ensure_solver() {
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace euf {
|
|||
virtual bool check(app* jst) = 0;
|
||||
virtual expr_ref_vector clause(app* jst) = 0;
|
||||
virtual void register_plugins(theory_checker& pc) = 0;
|
||||
virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.reset(); v.append(this->clause(jst)); return false; }
|
||||
virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.append(this->clause(jst)); return false; }
|
||||
};
|
||||
|
||||
class theory_checker {
|
||||
|
|
|
@ -349,6 +349,20 @@ namespace euf {
|
|||
si.uncache(literal(v, true));
|
||||
}
|
||||
|
||||
bool solver::decide(bool_var& var, lbool& phase) {
|
||||
for (auto const& th : m_solvers)
|
||||
if (th->decide(var, phase))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool solver::get_case_split(bool_var& var, lbool& phase) {
|
||||
for (auto const& th : m_solvers)
|
||||
if (th->get_case_split(var, phase))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void solver::asserted(literal l) {
|
||||
m_relevancy.asserted(l);
|
||||
if (!m_relevancy.is_relevant(l))
|
||||
|
@ -478,8 +492,13 @@ namespace euf {
|
|||
m_ackerman->cg_conflict_eh(a, b);
|
||||
switch (s().value(lit)) {
|
||||
case l_true:
|
||||
if (n->merge_tf() && !m.is_value(n->get_root()->get_expr()))
|
||||
m_egraph.merge(n, ante, to_ptr(lit));
|
||||
if (!n->merge_tf())
|
||||
break;
|
||||
if (m.is_value(n->get_root()->get_expr()))
|
||||
break;
|
||||
if (!ante)
|
||||
ante = mk_true();
|
||||
m_egraph.merge(n, ante, to_ptr(lit));
|
||||
break;
|
||||
case l_undef:
|
||||
case l_false:
|
||||
|
|
|
@ -100,6 +100,14 @@ namespace euf {
|
|||
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(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); }
|
||||
|
@ -119,6 +127,7 @@ namespace euf {
|
|||
sat::sat_internalizer& si;
|
||||
relevancy m_relevancy;
|
||||
smt_params m_config;
|
||||
local_search_config m_ls_config;
|
||||
euf::egraph m_egraph;
|
||||
trail_stack m_trail;
|
||||
stats m_stats;
|
||||
|
@ -251,7 +260,7 @@ namespace euf {
|
|||
constraint& mk_constraint(constraint*& c, constraint::kind_t k);
|
||||
constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); }
|
||||
constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); }
|
||||
constraint& lit_constraint(enode* n);
|
||||
constraint& lit_constraint(enode* n);
|
||||
|
||||
// user propagator
|
||||
void check_for_user_propagator() {
|
||||
|
@ -339,6 +348,7 @@ namespace euf {
|
|||
void add_assumptions(sat::literal_set& assumptions) override;
|
||||
bool tracking_assumptions() override;
|
||||
std::string reason_unknown() override { return m_reason_unknown; }
|
||||
lbool local_search(bool_vector& phase) override;
|
||||
|
||||
void propagate(literal lit, ext_justification_idx idx);
|
||||
bool propagate(enode* a, enode* b, ext_justification_idx idx);
|
||||
|
@ -359,6 +369,8 @@ namespace euf {
|
|||
void add_explain(size_t* p) { m_explain.push_back(p); }
|
||||
void reset_explain() { m_explain.reset(); }
|
||||
void set_eliminated(bool_var v) override;
|
||||
bool decide(bool_var& var, lbool& phase) override;
|
||||
bool get_case_split(bool_var& var, lbool& phase) override;
|
||||
void asserted(literal l) override;
|
||||
sat::check_result check() override;
|
||||
void push() override;
|
||||
|
@ -486,7 +498,7 @@ namespace euf {
|
|||
|
||||
// model construction
|
||||
void save_model(model_ref& mdl);
|
||||
void update_model(model_ref& mdl);
|
||||
void update_model(model_ref& mdl, bool validate);
|
||||
obj_map<expr, enode*> const& values2root();
|
||||
void model_updated(model_ref& mdl);
|
||||
expr* node2value(enode* n) const;
|
||||
|
@ -531,6 +543,10 @@ namespace euf {
|
|||
check_for_user_propagator();
|
||||
m_user_propagator->register_created(ceh);
|
||||
}
|
||||
void user_propagate_register_decide(user_propagator::decide_eh_t& ceh) {
|
||||
check_for_user_propagator();
|
||||
m_user_propagator->register_decide(ceh);
|
||||
}
|
||||
void user_propagate_register_expr(expr* e) {
|
||||
check_for_user_propagator();
|
||||
m_user_propagator->add_expr(e);
|
||||
|
@ -552,4 +568,3 @@ namespace euf {
|
|||
inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) {
|
||||
return s.display(out);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,11 @@ namespace pb {
|
|||
SASSERT(m_pb.is_pb(e));
|
||||
app* t = to_app(e);
|
||||
rational k = m_pb.get_k(t);
|
||||
if (!root && is_app(e)) {
|
||||
sat::literal lit = si.get_cached(to_app(e));
|
||||
if (lit != sat::null_literal)
|
||||
return sign ? ~lit : lit;
|
||||
}
|
||||
switch (t->get_decl_kind()) {
|
||||
case OP_AT_MOST_K:
|
||||
return convert_at_most_k(t, k, root, sign);
|
||||
|
@ -119,7 +124,7 @@ namespace pb {
|
|||
else {
|
||||
bool_var v = s().add_var(true);
|
||||
literal lit(v, sign);
|
||||
add_pb_ge(v, sign, wlits, k.get_unsigned());
|
||||
add_pb_ge(v, false, wlits, k.get_unsigned());
|
||||
TRACE("ba", tout << "root: " << root << " lit: " << lit << "\n";);
|
||||
return lit;
|
||||
}
|
||||
|
@ -146,7 +151,7 @@ namespace pb {
|
|||
else {
|
||||
sat::bool_var v = s().add_var(true);
|
||||
sat::literal lit(v, sign);
|
||||
add_pb_ge(v, sign, wlits, k.get_unsigned());
|
||||
add_pb_ge(v, false, wlits, k.get_unsigned());
|
||||
TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";);
|
||||
return lit;
|
||||
}
|
||||
|
|
|
@ -2039,7 +2039,7 @@ namespace pb {
|
|||
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]);
|
||||
for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]);
|
||||
init_use_lists();
|
||||
remove_unused_defs();
|
||||
// remove_unused_defs();
|
||||
set_non_external();
|
||||
elim_pure();
|
||||
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]);
|
||||
|
@ -2528,8 +2528,13 @@ namespace pb {
|
|||
}
|
||||
|
||||
void solver::remove_unused_defs() {
|
||||
if (incremental_mode()) return;
|
||||
if (incremental_mode())
|
||||
return;
|
||||
// remove constraints where indicator literal isn't used.
|
||||
NOT_IMPLEMENTED_YET();
|
||||
// TODO: #6675
|
||||
// need to add this inequality to the model reconstruction
|
||||
// stack in order to produce correct models.
|
||||
for (constraint* cp : m_constraints) {
|
||||
constraint& c = *cp;
|
||||
literal lit = c.lit();
|
||||
|
@ -2853,19 +2858,20 @@ namespace pb {
|
|||
|
||||
void solver::subsumes(pbc& p1, literal lit) {
|
||||
for (constraint* c : m_cnstr_use_list[lit.index()]) {
|
||||
if (c == &p1 || c->was_removed()) continue;
|
||||
bool s = false;
|
||||
if (c == &p1 || c->was_removed() || c->lit() != sat::null_literal)
|
||||
continue;
|
||||
bool sub = false;
|
||||
switch (c->tag()) {
|
||||
case pb::tag_t::card_t:
|
||||
s = subsumes(p1, c->to_card());
|
||||
sub = subsumes(p1, c->to_card());
|
||||
break;
|
||||
case pb::tag_t::pb_t:
|
||||
s = subsumes(p1, c->to_pb());
|
||||
sub = subsumes(p1, c->to_pb());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (s) {
|
||||
if (sub) {
|
||||
++m_stats.m_num_pb_subsumes;
|
||||
set_non_learned(p1);
|
||||
remove_constraint(*c, "subsumed");
|
||||
|
|
|
@ -357,8 +357,7 @@ namespace q {
|
|||
return expr_ref(m);
|
||||
}
|
||||
else if (!(*p)(*m_model, vars, fmls)) {
|
||||
TRACE("q", tout << "theory projection failed\n");
|
||||
return expr_ref(m);
|
||||
TRACE("q", tout << "theory projection failed - use value\n");
|
||||
}
|
||||
}
|
||||
for (app* v : vars) {
|
||||
|
@ -636,7 +635,7 @@ namespace q {
|
|||
if (m_model)
|
||||
return;
|
||||
m_model = alloc(model, m);
|
||||
ctx.update_model(m_model);
|
||||
ctx.update_model(m_model, false);
|
||||
}
|
||||
|
||||
void mbqi::init_solver() {
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace sat {
|
|||
virtual literal internalize(expr* e) = 0;
|
||||
virtual bool_var to_bool_var(expr* e) = 0;
|
||||
virtual bool_var add_bool_var(expr* e) = 0;
|
||||
virtual literal get_cached(app* t) const = 0;
|
||||
virtual bool is_cached(app* t, literal l) const = 0;
|
||||
virtual void cache(app* t, literal l) = 0;
|
||||
virtual void uncache(literal l) = 0;
|
||||
|
|
|
@ -18,6 +18,7 @@ Author:
|
|||
|
||||
#include "util/top_sort.h"
|
||||
#include "sat/smt/sat_smt.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
#include "ast/euf/euf_egraph.h"
|
||||
#include "model/model.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
|
@ -136,6 +137,17 @@ namespace euf {
|
|||
|
||||
sat::status status() const { return sat::status::th(false, get_id()); }
|
||||
|
||||
/**
|
||||
* Local search interface
|
||||
*/
|
||||
virtual void set_bool_search(sat::ddfw* ddfw) {}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -21,7 +21,7 @@ Author:
|
|||
namespace user_solver {
|
||||
|
||||
solver::solver(euf::solver& ctx) :
|
||||
th_euf_solver(ctx, symbol("user"), ctx.get_manager().mk_family_id("user"))
|
||||
th_euf_solver(ctx, symbol(user_propagator::plugin::name()), ctx.get_manager().mk_family_id(user_propagator::plugin::name()))
|
||||
{}
|
||||
|
||||
solver::~solver() {
|
||||
|
@ -92,24 +92,24 @@ namespace user_solver {
|
|||
|
||||
euf::enode* original_enode = bool_var2enode(var);
|
||||
|
||||
if (!is_attached_to_var(original_enode))
|
||||
if (!original_enode || !is_attached_to_var(original_enode))
|
||||
return false;
|
||||
|
||||
unsigned new_bit = 0; // ignored; currently no bv-support
|
||||
expr* e = bool_var2expr(var);
|
||||
expr* e = original_enode->get_expr();
|
||||
|
||||
m_decide_eh(m_user_context, this, &e, &new_bit, &phase);
|
||||
|
||||
euf::enode* new_enode = ctx.get_enode(e);
|
||||
|
||||
if (original_enode == new_enode)
|
||||
if (original_enode == new_enode || new_enode->bool_var() == sat::null_bool_var)
|
||||
return false;
|
||||
|
||||
var = new_enode->bool_var();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool solver::get_case_split(sat::bool_var& var, lbool &phase){
|
||||
bool solver::get_case_split(sat::bool_var& var, lbool& phase){
|
||||
if (!m_next_split_expr)
|
||||
return false;
|
||||
|
||||
|
@ -123,9 +123,12 @@ namespace user_solver {
|
|||
void solver::asserted(sat::literal lit) {
|
||||
if (!m_fixed_eh)
|
||||
return;
|
||||
force_push();
|
||||
auto* n = bool_var2enode(lit.var());
|
||||
euf::theory_var v = n->get_th_var(get_id());
|
||||
if (!m_id2justification.get(v, sat::literal_vector()).empty())
|
||||
// the core merged variables. We already issued the respective fixed callback for an equated variable
|
||||
return;
|
||||
force_push();
|
||||
sat::literal_vector lits;
|
||||
lits.push_back(lit);
|
||||
m_id2justification.setx(v, lits, sat::literal_vector());
|
||||
|
|
|
@ -276,11 +276,16 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
m_cache_trail.push_back(t);
|
||||
}
|
||||
|
||||
sat::literal get_cached(app* t) const override {
|
||||
sat::literal lit = sat::null_literal;
|
||||
m_app2lit.find(t, lit);
|
||||
return lit;
|
||||
}
|
||||
|
||||
bool is_cached(app* t, sat::literal l) const override {
|
||||
if (!m_app2lit.contains(t))
|
||||
return false;
|
||||
SASSERT(m_app2lit[t] == l);
|
||||
return true;
|
||||
sat::literal lit = get_cached(t);
|
||||
SASSERT(lit == sat::null_literal || l == lit);
|
||||
return l == lit;
|
||||
}
|
||||
|
||||
void convert_atom(expr * t, bool root, bool sign) {
|
||||
|
@ -987,7 +992,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
void update_model(model_ref& mdl) {
|
||||
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
|
||||
if (ext)
|
||||
ext->update_model(mdl);
|
||||
ext->update_model(mdl, true);
|
||||
}
|
||||
|
||||
void user_push() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue