3
0
Fork 0
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:
Jakob Rath 2023-05-26 15:58:09 +02:00
commit f54f33551e
308 changed files with 6606 additions and 18485 deletions

View file

@ -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 &&

View file

@ -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;

View file

@ -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();

View file

@ -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); }
};
}

View file

@ -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) {

View file

@ -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() {

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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'),

View file

@ -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(); }
};
}

View file

@ -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() {

View file

@ -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;

View file

@ -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:

View 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()();
};
};

View file

@ -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) {

View file

@ -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;
}
};
};

View file

@ -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

View file

@ -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);

View file

@ -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
View 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
View 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);
}
}

View file

@ -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) {

View file

@ -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;
};
}

View 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;
}
}

View file

@ -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) {

View file

@ -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() {

View file

@ -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 {

View file

@ -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:

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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");

View file

@ -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() {

View file

@ -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;

View file

@ -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 {

View file

@ -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());

View file

@ -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() {