mirror of
https://github.com/Z3Prover/z3
synced 2025-06-27 00:18:45 +00:00
Merge remote-tracking branch 'Z3Prover/polysat' into polysat
This commit is contained in:
commit
74ec28201e
41 changed files with 2206 additions and 661 deletions
|
@ -37,6 +37,7 @@ void bv_rewriter::updt_local_params(params_ref const & _p) {
|
||||||
m_ite2id = p.bv_ite2id();
|
m_ite2id = p.bv_ite2id();
|
||||||
m_le_extra = p.bv_le_extra();
|
m_le_extra = p.bv_le_extra();
|
||||||
m_le2extract = p.bv_le2extract();
|
m_le2extract = p.bv_le2extract();
|
||||||
|
m_le2extract = false; //
|
||||||
set_sort_sums(p.bv_sort_ac());
|
set_sort_sums(p.bv_sort_ac());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1724,6 +1725,9 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_le2extract)
|
||||||
|
return BR_FAILED;
|
||||||
|
|
||||||
if (!v1.is_zero() && new_args.size() == 1) {
|
if (!v1.is_zero() && new_args.size() == 1) {
|
||||||
v1 = m_util.norm(v1, sz);
|
v1 = m_util.norm(v1, sz);
|
||||||
#ifdef _TRACE
|
#ifdef _TRACE
|
||||||
|
|
|
@ -1208,6 +1208,11 @@ namespace dd {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return true iff p contains no variables other than v. */
|
||||||
|
bool pdd_manager::is_univariate_in(PDD p, unsigned v) {
|
||||||
|
return (is_val(p) || var(p) == v) && is_univariate(p);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push coefficients of univariate polynomial in order of ascending degree.
|
* Push coefficients of univariate polynomial in order of ascending degree.
|
||||||
* Example: a*x^2 + b*x + c ==> [ c, b, a ]
|
* Example: a*x^2 + b*x + c ==> [ c, b, a ]
|
||||||
|
@ -1719,7 +1724,7 @@ namespace dd {
|
||||||
unsigned pow;
|
unsigned pow;
|
||||||
if (val.is_power_of_two(pow) && pow > 10)
|
if (val.is_power_of_two(pow) && pow > 10)
|
||||||
return out << "2^" << pow;
|
return out << "2^" << pow;
|
||||||
for (int offset : {-1, 1})
|
for (int offset : {-2, -1, 1, 2})
|
||||||
if (val < m.max_value() && (val - offset).is_power_of_two(pow) && pow > 10)
|
if (val < m.max_value() && (val - offset).is_power_of_two(pow) && pow > 10)
|
||||||
return out << lparen() << "2^" << pow << (offset >= 0 ? "+" : "") << offset << rparen();
|
return out << lparen() << "2^" << pow << (offset >= 0 ? "+" : "") << offset << rparen();
|
||||||
rational neg_val = mod(-val, m.two_to_N());
|
rational neg_val = mod(-val, m.two_to_N());
|
||||||
|
|
|
@ -364,6 +364,7 @@ namespace dd {
|
||||||
bool is_monomial(PDD p);
|
bool is_monomial(PDD p);
|
||||||
|
|
||||||
bool is_univariate(PDD p);
|
bool is_univariate(PDD p);
|
||||||
|
bool is_univariate_in(PDD p, unsigned v);
|
||||||
void get_univariate_coefficients(PDD p, vector<rational>& coeff);
|
void get_univariate_coefficients(PDD p, vector<rational>& coeff);
|
||||||
|
|
||||||
// create an spoly r if leading monomials of a and b overlap
|
// create an spoly r if leading monomials of a and b overlap
|
||||||
|
@ -430,6 +431,7 @@ namespace dd {
|
||||||
bool is_binary() const { return m.is_binary(root); }
|
bool is_binary() const { return m.is_binary(root); }
|
||||||
bool is_monomial() const { return m.is_monomial(root); }
|
bool is_monomial() const { return m.is_monomial(root); }
|
||||||
bool is_univariate() const { return m.is_univariate(root); }
|
bool is_univariate() const { return m.is_univariate(root); }
|
||||||
|
bool is_univariate_in(unsigned v) const { return m.is_univariate_in(root, v); }
|
||||||
void get_univariate_coefficients(vector<rational>& coeff) const { m.get_univariate_coefficients(root, coeff); }
|
void get_univariate_coefficients(vector<rational>& coeff) const { m.get_univariate_coefficients(root, coeff); }
|
||||||
vector<rational> get_univariate_coefficients() const { vector<rational> coeff; m.get_univariate_coefficients(root, coeff); return coeff; }
|
vector<rational> get_univariate_coefficients() const { vector<rational> coeff; m.get_univariate_coefficients(root, coeff); return coeff; }
|
||||||
bool is_never_zero() const { return m.is_never_zero(root); }
|
bool is_never_zero() const { return m.is_never_zero(root); }
|
||||||
|
|
|
@ -53,13 +53,13 @@ namespace polysat {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
substitution(dd::pdd_manager& m);
|
substitution(dd::pdd_manager& m);
|
||||||
substitution add(pvar var, rational const& value) const;
|
[[nodiscard]] substitution add(pvar var, rational const& value) const;
|
||||||
pdd apply_to(pdd const& p) const;
|
[[nodiscard]] pdd apply_to(pdd const& p) const;
|
||||||
|
|
||||||
bool contains(pvar var) const;
|
[[nodiscard]] bool contains(pvar var) const;
|
||||||
bool value(pvar var, rational& out_value) const;
|
[[nodiscard]] bool value(pvar var, rational& out_value) const;
|
||||||
|
|
||||||
bool empty() const { return m_subst.is_one(); }
|
[[nodiscard]] bool empty() const { return m_subst.is_one(); }
|
||||||
|
|
||||||
pdd const& to_pdd() const { return m_subst; }
|
pdd const& to_pdd() const { return m_subst; }
|
||||||
unsigned bit_width() const { return to_pdd().power_of_2(); }
|
unsigned bit_width() const { return to_pdd().power_of_2(); }
|
||||||
|
@ -77,7 +77,7 @@ namespace polysat {
|
||||||
/** Full variable assignment, may include variables of varying bit widths. */
|
/** Full variable assignment, may include variables of varying bit widths. */
|
||||||
class assignment {
|
class assignment {
|
||||||
solver* m_solver;
|
solver* m_solver;
|
||||||
vector<assignment_item_t> m_pairs; // TODO: do we still need this?
|
vector<assignment_item_t> m_pairs;
|
||||||
mutable scoped_ptr_vector<substitution> m_subst;
|
mutable scoped_ptr_vector<substitution> m_subst;
|
||||||
vector<substitution> m_subst_trail;
|
vector<substitution> m_subst_trail;
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void bool_var_manager::eval(sat::literal lit, unsigned lvl) {
|
void bool_var_manager::eval(sat::literal lit, unsigned lvl) {
|
||||||
LOG_V("Evaluate " << lit << " @ " << lvl);
|
LOG_V(10, "Evaluate " << lit << " @ " << lvl);
|
||||||
assign(kind_t::evaluation, lit, lvl, nullptr);
|
assign(kind_t::evaluation, lit, lvl, nullptr);
|
||||||
SASSERT(is_evaluation(lit));
|
SASSERT(is_evaluation(lit));
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,15 +75,23 @@ namespace polysat {
|
||||||
m_literals.push_back(c.blit());
|
m_literals.push_back(c.blit());
|
||||||
}
|
}
|
||||||
|
|
||||||
void clause_builder::insert_eval(sat::literal lit, bool status) {
|
void clause_builder::insert_eval(sat::literal lit) {
|
||||||
insert_eval(m_solver->lit2cnstr(lit), status);
|
insert_eval(m_solver->lit2cnstr(lit));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clause_builder::insert_eval(signed_constraint c, bool status) {
|
void clause_builder::insert_eval(signed_constraint c) {
|
||||||
if (c.bvalue(*m_solver) == l_undef) {
|
if (c.bvalue(*m_solver) == l_undef)
|
||||||
sat::literal lit = c.blit();
|
m_solver->assign_eval(~c.blit());
|
||||||
m_solver->assign_eval(status ? lit : ~lit);
|
insert(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clause_builder::insert_try_eval(sat::literal lit) {
|
||||||
|
insert_eval(m_solver->lit2cnstr(lit));
|
||||||
|
}
|
||||||
|
|
||||||
|
void clause_builder::insert_try_eval(signed_constraint c) {
|
||||||
|
if (c.bvalue(*m_solver) == l_undef && c.is_currently_false(*m_solver))
|
||||||
|
m_solver->assign_eval(~c.blit());
|
||||||
insert(c);
|
insert(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,19 +51,20 @@ namespace polysat {
|
||||||
void insert(signed_constraint c);
|
void insert(signed_constraint c);
|
||||||
void insert(inequality const& i) { insert(i.as_signed_constraint()); }
|
void insert(inequality const& i) { insert(i.as_signed_constraint()); }
|
||||||
|
|
||||||
/// Insert constraints into the clause.
|
|
||||||
/// If they are not yet on the search stack, add them as evaluated to the given status.
|
|
||||||
/// \pre Constraint must evaluate to true or false according to the given status in the current assignment.
|
|
||||||
void insert_eval(sat::literal lit, bool status);
|
|
||||||
void insert_eval(signed_constraint c, bool status);
|
|
||||||
|
|
||||||
/// Insert constraints into the clause.
|
/// Insert constraints into the clause.
|
||||||
/// If they are not yet on the search stack, add them as evaluated to \b false.
|
/// If they are not yet on the search stack, add them as evaluated to \b false.
|
||||||
/// \pre Constraint must be \b false in the current assignment.
|
/// \pre Constraint must be \b false in the current assignment.
|
||||||
void insert_eval(sat::literal lit) { insert_eval(lit, false); }
|
void insert_eval(sat::literal lit);
|
||||||
void insert_eval(signed_constraint c) { insert_eval(c, false); }
|
void insert_eval(signed_constraint c);
|
||||||
void insert_eval(inequality const& i) { insert_eval(i.as_signed_constraint()); }
|
void insert_eval(inequality const& i) { insert_eval(i.as_signed_constraint()); }
|
||||||
|
|
||||||
|
/// Insert constraints into the clause.
|
||||||
|
/// If possible, evaluate them as in insert_eval.
|
||||||
|
/// Evaluation might not be possible if not all variables in the constraint are assigned.
|
||||||
|
/// \pre Constraint must be \b non-true in the current assignment.
|
||||||
|
void insert_try_eval(sat::literal lit);
|
||||||
|
void insert_try_eval(signed_constraint lit);
|
||||||
|
|
||||||
using const_iterator = decltype(m_literals)::const_iterator;
|
using const_iterator = decltype(m_literals)::const_iterator;
|
||||||
const_iterator begin() const { return m_literals.begin(); }
|
const_iterator begin() const { return m_literals.begin(); }
|
||||||
const_iterator end() const { return m_literals.end(); }
|
const_iterator end() const { return m_literals.end(); }
|
||||||
|
|
|
@ -186,6 +186,8 @@ namespace polysat {
|
||||||
SASSERT(s.at_base_level());
|
SASSERT(s.at_base_level());
|
||||||
m_level = s.m_level;
|
m_level = s.m_level;
|
||||||
SASSERT(!empty());
|
SASSERT(!empty());
|
||||||
|
// TODO: logger().begin_conflict???
|
||||||
|
// TODO: check uses of logger().begin_conflict(). sometimes we call it before adding constraints, sometimes after...
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict::init(signed_constraint c) {
|
void conflict::init(signed_constraint c) {
|
||||||
|
@ -193,17 +195,6 @@ namespace polysat {
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
m_level = s.m_level;
|
m_level = s.m_level;
|
||||||
m_narrow_queue.push_back(c.blit()); // if the conflict is only due to a missed propagation of c
|
m_narrow_queue.push_back(c.blit()); // if the conflict is only due to a missed propagation of c
|
||||||
set_impl(c);
|
|
||||||
logger().begin_conflict();
|
|
||||||
}
|
|
||||||
|
|
||||||
void conflict::set(signed_constraint c) {
|
|
||||||
SASSERT(!empty());
|
|
||||||
remove_all();
|
|
||||||
set_impl(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
void conflict::set_impl(signed_constraint c) {
|
|
||||||
if (c.bvalue(s) == l_false) {
|
if (c.bvalue(s) == l_false) {
|
||||||
// boolean conflict
|
// boolean conflict
|
||||||
// This case should not happen:
|
// This case should not happen:
|
||||||
|
@ -219,6 +210,7 @@ namespace polysat {
|
||||||
insert_vars(c);
|
insert_vars(c);
|
||||||
}
|
}
|
||||||
SASSERT(!empty());
|
SASSERT(!empty());
|
||||||
|
logger().begin_conflict();
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict::init(clause const& cl) {
|
void conflict::init(clause const& cl) {
|
||||||
|
@ -234,31 +226,24 @@ namespace polysat {
|
||||||
logger().begin_conflict();
|
logger().begin_conflict();
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict::init(pvar v, bool by_viable_fallback) {
|
void conflict::init_by_viable_interval(pvar v) {
|
||||||
LOG("Conflict: viable v" << v);
|
LOG("Conflict: viable_interval v" << v);
|
||||||
SASSERT(empty());
|
SASSERT(empty());
|
||||||
|
SASSERT(!s.is_assigned(v));
|
||||||
m_level = s.m_level;
|
m_level = s.m_level;
|
||||||
if (by_viable_fallback) {
|
logger().begin_conflict(header_with_var("viable_interval v", v));
|
||||||
logger().begin_conflict(header_with_var("unsat core from viable fallback for v", v));
|
VERIFY(s.m_viable.resolve_interval(v, *this));
|
||||||
// Conflict detected by viable fallback:
|
SASSERT(!empty());
|
||||||
// initial conflict is the unsat core of the univariate solver,
|
revert_pvar(v); // at this point, v is not assigned
|
||||||
// and the assignment (under which the constraints are univariate in v)
|
}
|
||||||
// TODO:
|
|
||||||
// - currently we add variables directly, which is sound:
|
void conflict::init_by_viable_fallback(pvar v, univariate_solver& us) {
|
||||||
// e.g.: v^2 + w^2 == 0; w := 1
|
LOG("Conflict: viable_fallback v" << v);
|
||||||
// - but we could use side constraints on the coefficients instead (coefficients when viewed as polynomial over v):
|
SASSERT(empty());
|
||||||
// e.g.: v^2 + w^2 == 0; w^2 == 1
|
SASSERT(!s.is_assigned(v));
|
||||||
signed_constraints unsat_core = s.m_viable_fallback.unsat_core(v);
|
m_level = s.m_level;
|
||||||
for (auto c : unsat_core) {
|
logger().begin_conflict(header_with_var("viable_fallback v", v));
|
||||||
insert(c);
|
VERIFY(s.m_viable.resolve_fallback(v, us, *this));
|
||||||
insert_vars(c);
|
|
||||||
}
|
|
||||||
SASSERT(!m_vars.contains(v));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger().begin_conflict(header_with_var("forbidden interval lemma for v", v));
|
|
||||||
VERIFY(s.m_viable.resolve(v, *this));
|
|
||||||
}
|
|
||||||
SASSERT(!empty());
|
SASSERT(!empty());
|
||||||
revert_pvar(v); // at this point, v is not assigned
|
revert_pvar(v); // at this point, v is not assigned
|
||||||
}
|
}
|
||||||
|
@ -318,8 +303,14 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void conflict::add_lemma(char const* name, clause_ref lemma) {
|
void conflict::add_lemma(char const* name, clause_ref lemma) {
|
||||||
|
|
||||||
|
for (auto lit : *lemma)
|
||||||
|
if (s.m_bvars.is_true(lit))
|
||||||
|
verbose_stream() << "REDUNDANT lemma " << lit << " : " << show_deref(lemma) << "\n";
|
||||||
|
|
||||||
LOG_H3("Lemma " << (name ? name : "<unknown>") << ": " << show_deref(lemma));
|
LOG_H3("Lemma " << (name ? name : "<unknown>") << ": " << show_deref(lemma));
|
||||||
SASSERT(lemma);
|
SASSERT(lemma);
|
||||||
|
s.m_simplify_clause.apply(*lemma);
|
||||||
lemma->set_redundant(true);
|
lemma->set_redundant(true);
|
||||||
for (sat::literal lit : *lemma) {
|
for (sat::literal lit : *lemma) {
|
||||||
LOG(lit_pp(s, lit));
|
LOG(lit_pp(s, lit));
|
||||||
|
|
|
@ -85,7 +85,7 @@ namespace polysat {
|
||||||
scoped_ptr<conflict_resolver> m_resolver;
|
scoped_ptr<conflict_resolver> m_resolver;
|
||||||
|
|
||||||
// current conflict core consists of m_literals and m_vars
|
// current conflict core consists of m_literals and m_vars
|
||||||
indexed_uint_set m_literals; // set of boolean literals in the conflict
|
indexed_uint_set m_literals; // set of boolean literals in the conflict; TODO: why not sat::literal_set
|
||||||
uint_set m_vars; // variable assignments used as premises, shorthand for literals (x := v)
|
uint_set m_vars; // variable assignments used as premises, shorthand for literals (x := v)
|
||||||
|
|
||||||
unsigned_vector m_var_occurrences; // for each variable, the number of constraints in m_literals that contain it
|
unsigned_vector m_var_occurrences; // for each variable, the number of constraints in m_literals that contain it
|
||||||
|
@ -102,8 +102,6 @@ namespace polysat {
|
||||||
// Level at which the conflict was discovered
|
// Level at which the conflict was discovered
|
||||||
unsigned m_level = UINT_MAX;
|
unsigned m_level = UINT_MAX;
|
||||||
|
|
||||||
void set_impl(signed_constraint c);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
conflict(solver& s);
|
conflict(solver& s);
|
||||||
~conflict();
|
~conflict();
|
||||||
|
@ -133,11 +131,10 @@ namespace polysat {
|
||||||
void init(signed_constraint c);
|
void init(signed_constraint c);
|
||||||
/** boolean conflict with the given clause */
|
/** boolean conflict with the given clause */
|
||||||
void init(clause const& cl);
|
void init(clause const& cl);
|
||||||
/** conflict because there is no viable value for the variable v */
|
/** conflict because there is no viable value for the variable v, by interval reasoning */
|
||||||
void init(pvar v, bool by_viable_fallback);
|
void init_by_viable_interval(pvar v);
|
||||||
|
/** conflict because there is no viable value for the variable v, by fallback solver */
|
||||||
/** replace the current conflict by a single constraint */
|
void init_by_viable_fallback(pvar v, univariate_solver& us);
|
||||||
void set(signed_constraint c);
|
|
||||||
|
|
||||||
bool contains(signed_constraint c) const { SASSERT(c); return contains(c.blit()); }
|
bool contains(signed_constraint c) const { SASSERT(c); return contains(c.blit()); }
|
||||||
bool contains(sat::literal lit) const;
|
bool contains(sat::literal lit) const;
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace polysat {
|
||||||
/** The boolean variable associated to this constraint */
|
/** The boolean variable associated to this constraint */
|
||||||
sat::bool_var m_bvar = sat::null_bool_var;
|
sat::bool_var m_bvar = sat::null_bool_var;
|
||||||
|
|
||||||
constraint(constraint_manager& m, ckind_t k): m_kind(k) {}
|
constraint(ckind_t k): m_kind(k) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~constraint() {}
|
virtual ~constraint() {}
|
||||||
|
@ -115,9 +115,11 @@ namespace polysat {
|
||||||
bool is_pwatched() const { return m_is_pwatched; }
|
bool is_pwatched() const { return m_is_pwatched; }
|
||||||
void set_pwatched(bool f) { m_is_pwatched = f; }
|
void set_pwatched(bool f) { m_is_pwatched = f; }
|
||||||
|
|
||||||
/// Assuming the constraint is univariate under the current assignment of 's',
|
/**
|
||||||
/// adds the constraint to the univariate solver 'us'.
|
* If the constraint is univariate in variable 'v' under the current assignment of 's',
|
||||||
virtual void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const = 0;
|
* add the constraint to the univariate solver 'us'.
|
||||||
|
*/
|
||||||
|
virtual void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
|
inline std::ostream& operator<<(std::ostream& out, constraint const& c) { return c.display(out); }
|
||||||
|
@ -162,7 +164,7 @@ namespace polysat {
|
||||||
void narrow(solver& s, bool first) { get()->narrow(s, is_positive(), first); }
|
void narrow(solver& s, bool first) { get()->narrow(s, is_positive(), first); }
|
||||||
clause_ref produce_lemma(solver& s, assignment const& a) { return get()->produce_lemma(s, a, is_positive()); }
|
clause_ref produce_lemma(solver& s, assignment const& a) { return get()->produce_lemma(s, a, is_positive()); }
|
||||||
|
|
||||||
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(s, us, dep, is_positive()); }
|
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(v, s, us, dep, is_positive()); }
|
||||||
|
|
||||||
unsigned_vector const& vars() const { return m_constraint->vars(); }
|
unsigned_vector const& vars() const { return m_constraint->vars(); }
|
||||||
unsigned var(unsigned idx) const { return m_constraint->var(idx); }
|
unsigned var(unsigned idx) const { return m_constraint->var(idx); }
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace polysat {
|
||||||
|
|
||||||
/** Add constraint to per-level storage */
|
/** Add constraint to per-level storage */
|
||||||
void constraint_manager::store(constraint* c) {
|
void constraint_manager::store(constraint* c) {
|
||||||
LOG_V("Store constraint: " << show_deref(c));
|
LOG_V(20, "Store constraint: " << show_deref(c));
|
||||||
m_constraints.push_back(c);
|
m_constraints.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Look up constraint among stored constraints. */
|
/** Look up constraint among stored constraints. */
|
||||||
constraint* constraint_manager::dedup(constraint* c1) {
|
constraint* constraint_manager::dedup_store(constraint* c1) {
|
||||||
constraint* c2 = nullptr;
|
constraint* c2 = nullptr;
|
||||||
if (m_dedup.constraints.find(c1, c2)) {
|
if (m_dedup.constraints.find(c1, c2)) {
|
||||||
dealloc(c1);
|
dealloc(c1);
|
||||||
|
@ -246,6 +246,15 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Find stored constraint */
|
||||||
|
constraint* constraint_manager::dedup_find(constraint* c1) const {
|
||||||
|
constraint* c = nullptr;
|
||||||
|
if (!m_dedup.constraints.find(c1, c)) {
|
||||||
|
SASSERT(c == nullptr);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
void constraint_manager::gc() {
|
void constraint_manager::gc() {
|
||||||
LOG_H1("gc");
|
LOG_H1("gc");
|
||||||
gc_clauses();
|
gc_clauses();
|
||||||
|
@ -294,7 +303,7 @@ namespace polysat {
|
||||||
pdd lhs = a;
|
pdd lhs = a;
|
||||||
pdd rhs = b;
|
pdd rhs = b;
|
||||||
ule_constraint::simplify(is_positive, lhs, rhs);
|
ule_constraint::simplify(is_positive, lhs, rhs);
|
||||||
return { dedup(alloc(ule_constraint, *this, lhs, rhs)), is_positive };
|
return { dedup_store(alloc(ule_constraint, lhs, rhs)), is_positive };
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_constraint constraint_manager::eq(pdd const& p) {
|
signed_constraint constraint_manager::eq(pdd const& p) {
|
||||||
|
@ -305,6 +314,19 @@ namespace polysat {
|
||||||
return ~ule(b, a);
|
return ~ule(b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signed_constraint constraint_manager::find_eq(pdd const& p) const {
|
||||||
|
return find_ule(p, p.manager().zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
signed_constraint constraint_manager::find_ule(pdd const& a, pdd const& b) const {
|
||||||
|
bool is_positive = true;
|
||||||
|
pdd lhs = a;
|
||||||
|
pdd rhs = b;
|
||||||
|
ule_constraint::simplify(is_positive, lhs, rhs);
|
||||||
|
ule_constraint tmp(lhs, rhs); // TODO: this still allocates ule_constraint::m_vars
|
||||||
|
return { dedup_find(&tmp), is_positive };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* encode that the i'th bit of p is 1.
|
* encode that the i'th bit of p is 1.
|
||||||
* It holds if p << (K - i - 1) >= 2^{K-1}, where K is the bit-width.
|
* It holds if p << (K - i - 1) >= 2^{K-1}, where K is the bit-width.
|
||||||
|
@ -317,19 +339,19 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_constraint constraint_manager::umul_ovfl(pdd const& a, pdd const& b) {
|
signed_constraint constraint_manager::umul_ovfl(pdd const& a, pdd const& b) {
|
||||||
return { dedup(alloc(umul_ovfl_constraint, *this, a, b)), true };
|
return { dedup_store(alloc(umul_ovfl_constraint, a, b)), true };
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_constraint constraint_manager::smul_ovfl(pdd const& a, pdd const& b) {
|
signed_constraint constraint_manager::smul_ovfl(pdd const& a, pdd const& b) {
|
||||||
return { dedup(alloc(smul_fl_constraint, *this, a, b, true)), true };
|
return { dedup_store(alloc(smul_fl_constraint, a, b, true)), true };
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_constraint constraint_manager::smul_udfl(pdd const& a, pdd const& b) {
|
signed_constraint constraint_manager::smul_udfl(pdd const& a, pdd const& b) {
|
||||||
return { dedup(alloc(smul_fl_constraint, *this, a, b, false)), true };
|
return { dedup_store(alloc(smul_fl_constraint, a, b, false)), true };
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_constraint constraint_manager::mk_op_constraint(op_constraint::code op, pdd const& p, pdd const& q, pdd const& r) {
|
signed_constraint constraint_manager::mk_op_constraint(op_constraint::code op, pdd const& p, pdd const& q, pdd const& r) {
|
||||||
return { dedup(alloc(op_constraint, *this, op, p, q, r)), true };
|
return { dedup_store(alloc(op_constraint, op, p, q, r)), true };
|
||||||
}
|
}
|
||||||
|
|
||||||
// To do signed comparison of bitvectors, flip the msb and do unsigned comparison:
|
// To do signed comparison of bitvectors, flip the msb and do unsigned comparison:
|
||||||
|
@ -405,6 +427,8 @@ namespace polysat {
|
||||||
// addition does not overflow in (b*q) + r; for now expressed as: r <= bq+r
|
// addition does not overflow in (b*q) + r; for now expressed as: r <= bq+r
|
||||||
// b ≠ 0 ==> r < b
|
// b ≠ 0 ==> r < b
|
||||||
// b = 0 ==> q = -1
|
// b = 0 ==> q = -1
|
||||||
|
// TODO: when a,b become evaluable, can we actually propagate q,r? doesn't seem like it.
|
||||||
|
// Maybe we need something like an op_constraint for better propagation.
|
||||||
s.add_clause(eq(b * q + r - a), false);
|
s.add_clause(eq(b * q + r - a), false);
|
||||||
s.add_clause(~umul_ovfl(b, q), false);
|
s.add_clause(~umul_ovfl(b, q), false);
|
||||||
// r <= b*q+r
|
// r <= b*q+r
|
||||||
|
|
|
@ -67,9 +67,9 @@ namespace polysat {
|
||||||
constraint* get_bv2c(sat::bool_var bv) const;
|
constraint* get_bv2c(sat::bool_var bv) const;
|
||||||
|
|
||||||
void store(constraint* c);
|
void store(constraint* c);
|
||||||
void erase(constraint* c);
|
|
||||||
|
|
||||||
constraint* dedup(constraint* c);
|
constraint* dedup_store(constraint* c);
|
||||||
|
constraint* dedup_find(constraint* c) const;
|
||||||
|
|
||||||
void gc_constraints();
|
void gc_constraints();
|
||||||
void gc_clauses();
|
void gc_clauses();
|
||||||
|
@ -102,6 +102,10 @@ namespace polysat {
|
||||||
constraint* lookup(sat::bool_var var) const;
|
constraint* lookup(sat::bool_var var) const;
|
||||||
signed_constraint lookup(sat::literal lit) const;
|
signed_constraint lookup(sat::literal lit) const;
|
||||||
|
|
||||||
|
/** Find constraint p == 0; returns null if it doesn't exist yet */
|
||||||
|
signed_constraint find_eq(pdd const& p) const;
|
||||||
|
signed_constraint find_ule(pdd const& a, pdd const& b) const;
|
||||||
|
|
||||||
signed_constraint eq(pdd const& p);
|
signed_constraint eq(pdd const& p);
|
||||||
signed_constraint ule(pdd const& a, pdd const& b);
|
signed_constraint ule(pdd const& a, pdd const& b);
|
||||||
signed_constraint ult(pdd const& a, pdd const& b);
|
signed_constraint ult(pdd const& a, pdd const& b);
|
||||||
|
@ -126,9 +130,65 @@ namespace polysat {
|
||||||
constraint* const* begin() const { return m_constraints.data(); }
|
constraint* const* begin() const { return m_constraints.data(); }
|
||||||
constraint* const* end() const { return m_constraints.data() + m_constraints.size(); }
|
constraint* const* end() const { return m_constraints.data() + m_constraints.size(); }
|
||||||
|
|
||||||
using clause_iterator = decltype(m_clauses)::const_iterator;
|
class clause_iterator {
|
||||||
clause_iterator clauses_begin() const { return m_clauses.begin(); }
|
friend class constraint_manager;
|
||||||
clause_iterator clauses_end() const { return m_clauses.end(); }
|
using storage_t = decltype(constraint_manager::m_clauses);
|
||||||
|
|
||||||
|
using outer_iterator = storage_t::const_iterator;
|
||||||
|
outer_iterator outer_it;
|
||||||
|
outer_iterator outer_end;
|
||||||
|
|
||||||
|
using inner_iterator = storage_t::data_t::const_iterator;
|
||||||
|
inner_iterator inner_it;
|
||||||
|
inner_iterator inner_end;
|
||||||
|
|
||||||
|
clause_iterator(storage_t const& cls, bool begin) {
|
||||||
|
if (begin) {
|
||||||
|
outer_it = cls.begin();
|
||||||
|
outer_end = cls.end();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outer_it = cls.end();
|
||||||
|
outer_end = cls.end();
|
||||||
|
}
|
||||||
|
begin_inner();
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin_inner() {
|
||||||
|
if (outer_it == outer_end) {
|
||||||
|
inner_it = nullptr;
|
||||||
|
inner_end = nullptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inner_it = outer_it->begin();
|
||||||
|
inner_end = outer_it->end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
clause const& operator*() const {
|
||||||
|
return *inner_it->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
clause_iterator& operator++() {
|
||||||
|
++inner_it;
|
||||||
|
while (inner_it == inner_end && outer_it != outer_end) {
|
||||||
|
++outer_it;
|
||||||
|
begin_inner();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(clause_iterator const& other) const {
|
||||||
|
return outer_it == other.outer_it && inner_it == other.inner_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(clause_iterator const& other) const {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
clause_iterator clauses_begin() const { return clause_iterator(m_clauses, true); }
|
||||||
|
clause_iterator clauses_end() const { return clause_iterator(m_clauses, false); }
|
||||||
|
|
||||||
class clauses_t {
|
class clauses_t {
|
||||||
constraint_manager const* m_cm;
|
constraint_manager const* m_cm;
|
||||||
|
|
|
@ -9,7 +9,7 @@ Module Name:
|
||||||
|
|
||||||
Author:
|
Author:
|
||||||
|
|
||||||
Jakob Rath 2021-04-6
|
Jakob Rath 2021-04-06
|
||||||
Nikolaj Bjorner (nbjorner) 2021-03-19
|
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
@ -21,7 +21,7 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* \param[in] c Original constraint
|
* \param[in] c Original constraint
|
||||||
* \param[in] v Variable that is bounded by constraint
|
* \param[in] v Variable that is bounded by constraint
|
||||||
|
@ -38,7 +38,7 @@ namespace polysat {
|
||||||
|
|
||||||
bool forbidden_intervals::get_interval_umul_ovfl(signed_constraint const& c, pvar v, fi_record& fi) {
|
bool forbidden_intervals::get_interval_umul_ovfl(signed_constraint const& c, pvar v, fi_record& fi) {
|
||||||
|
|
||||||
backtrack _backtrack(fi.side_cond);
|
backtrack _backtrack(fi.side_cond);
|
||||||
|
|
||||||
fi.coeff = 1;
|
fi.coeff = 1;
|
||||||
fi.src = c;
|
fi.src = c;
|
||||||
|
@ -56,7 +56,7 @@ namespace polysat {
|
||||||
std::swap(a1, a2);
|
std::swap(a1, a2);
|
||||||
std::swap(e1, e2);
|
std::swap(e1, e2);
|
||||||
std::swap(b1, b2);
|
std::swap(b1, b2);
|
||||||
std::swap(ok1, ok2);
|
std::swap(ok1, ok2);
|
||||||
}
|
}
|
||||||
if (ok1 && !ok2 && a1.is_one() && b1.is_zero()) {
|
if (ok1 && !ok2 && a1.is_one() && b1.is_zero()) {
|
||||||
if (c.is_positive()) {
|
if (c.is_positive()) {
|
||||||
|
@ -69,8 +69,8 @@ namespace polysat {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok1 || !ok2)
|
if (!ok1 || !ok2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,10 +129,10 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
static char const* _last_function = "";
|
static char const* _last_function = "";
|
||||||
|
|
||||||
bool forbidden_intervals::get_interval_ule(signed_constraint const& c, pvar v, fi_record& fi) {
|
bool forbidden_intervals::get_interval_ule(signed_constraint const& c, pvar v, fi_record& fi) {
|
||||||
|
|
||||||
backtrack _backtrack(fi.side_cond);
|
backtrack _backtrack(fi.side_cond);
|
||||||
|
|
||||||
fi.coeff = 1;
|
fi.coeff = 1;
|
||||||
fi.src = c;
|
fi.src = c;
|
||||||
|
@ -166,11 +166,11 @@ namespace polysat {
|
||||||
_backtrack.released = true;
|
_backtrack.released = true;
|
||||||
|
|
||||||
// v > q
|
// v > q
|
||||||
if (ok1 && !ok2 && match_non_zero(c, a1, b1, e1, fi))
|
if (false && ok1 && !ok2 && match_non_zero(c, a1, b1, e1, c->to_ule().rhs(), fi))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// p > v
|
// p > v
|
||||||
if (!ok1 && ok2 && match_non_max(c, a2, b2, e2, fi))
|
if (false && !ok1 && ok2 && match_non_max(c, c->to_ule().lhs(), a2, b2, e2, fi))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!ok1 || !ok2 || (a1.is_zero() && a2.is_zero())) {
|
if (!ok1 || !ok2 || (a1.is_zero() && a2.is_zero())) {
|
||||||
|
@ -185,6 +185,11 @@ namespace polysat {
|
||||||
if (match_zero(c, a1, b1, e1, a2, b2, e2, fi))
|
if (match_zero(c, a1, b1, e1, a2, b2, e2, fi))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// -1 <= a*v + b, a odd
|
||||||
|
// -1 > a*v + b, a odd
|
||||||
|
if (match_max(c, a1, b1, e1, a2, b2, e2, fi))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (match_linear1(c, a1, b1, e1, a2, b2, e2, fi))
|
if (match_linear1(c, a1, b1, e1, a2, b2, e2, fi))
|
||||||
return true;
|
return true;
|
||||||
if (match_linear2(c, a1, b1, e1, a2, b2, e2, fi))
|
if (match_linear2(c, a1, b1, e1, a2, b2, e2, fi))
|
||||||
|
@ -231,7 +236,7 @@ namespace polysat {
|
||||||
out_side_cond.push_back(s.eq(q, r));
|
out_side_cond.push_back(s.eq(q, r));
|
||||||
q = r;
|
q = r;
|
||||||
}
|
}
|
||||||
auto b = s.subst(e);
|
auto b = s.subst(e);
|
||||||
return std::tuple(b.is_val(), q.val(), e, b);
|
return std::tuple(b.is_val(), q.val(), e, b);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -239,7 +244,7 @@ namespace polysat {
|
||||||
signed_constraint const& c, bool is_trivial, rational & coeff,
|
signed_constraint const& c, bool is_trivial, rational & coeff,
|
||||||
rational & lo_val, pdd & lo,
|
rational & lo_val, pdd & lo,
|
||||||
rational & hi_val, pdd & hi) {
|
rational & hi_val, pdd & hi) {
|
||||||
|
|
||||||
dd::pdd_manager& m = lo.manager();
|
dd::pdd_manager& m = lo.manager();
|
||||||
|
|
||||||
if (is_trivial) {
|
if (is_trivial) {
|
||||||
|
@ -256,7 +261,7 @@ namespace polysat {
|
||||||
rational pow2 = m.max_value() + 1;
|
rational pow2 = m.max_value() + 1;
|
||||||
|
|
||||||
if (coeff > pow2/2) {
|
if (coeff > pow2/2) {
|
||||||
|
|
||||||
coeff = pow2 - coeff;
|
coeff = pow2 - coeff;
|
||||||
SASSERT(coeff > 0);
|
SASSERT(coeff > 0);
|
||||||
// Transform according to: y \in [l;u[ <=> -y \in [1-u;1-l[
|
// Transform according to: y \in [l;u[ <=> -y \in [1-u;1-l[
|
||||||
|
@ -282,10 +287,10 @@ namespace polysat {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Match e1 + t <= e2, with t = a1*y
|
* Match e1 + t <= e2, with t = a1*y
|
||||||
* condition for empty/full: e2 == -1
|
* condition for empty/full: e2 == -1
|
||||||
*/
|
*/
|
||||||
bool forbidden_intervals::match_linear1(signed_constraint const& c,
|
bool forbidden_intervals::match_linear1(signed_constraint const& c,
|
||||||
rational const & a1, pdd const& b1, pdd const& e1,
|
rational const & a1, pdd const& b1, pdd const& e1,
|
||||||
rational const & a2, pdd const& b2, pdd const& e2,
|
rational const & a2, pdd const& b2, pdd const& e2,
|
||||||
fi_record& fi) {
|
fi_record& fi) {
|
||||||
_last_function = __func__;
|
_last_function = __func__;
|
||||||
|
@ -382,10 +387,10 @@ namespace polysat {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a*v <= 0, a odd
|
* a*v <= 0, a odd
|
||||||
* forbidden interval for v is [1,0[
|
* forbidden interval for v is [1;0[
|
||||||
*
|
*
|
||||||
* a*v + b <= 0, a odd
|
* a*v + b <= 0, a odd
|
||||||
* forbidden interval for v is [n+1,n[ where n = -b * a^-1
|
* forbidden interval for v is [n+1;n[ where n = -b * a^-1
|
||||||
*
|
*
|
||||||
* TODO: extend to
|
* TODO: extend to
|
||||||
* 2^k*a*v <= 0, a odd
|
* 2^k*a*v <= 0, a odd
|
||||||
|
@ -403,7 +408,7 @@ namespace polysat {
|
||||||
rational a1_inv;
|
rational a1_inv;
|
||||||
VERIFY(a1.mult_inverse(m.power_of_2(), a1_inv));
|
VERIFY(a1.mult_inverse(m.power_of_2(), a1_inv));
|
||||||
|
|
||||||
// interval for a*v + b > 0 is [n,n+1[ where n = -b * a^-1
|
// interval for a*v + b > 0 is [n;n+1[ where n = -b * a^-1
|
||||||
rational lo_val = mod(-b1.val() * a1_inv, mod_value);
|
rational lo_val = mod(-b1.val() * a1_inv, mod_value);
|
||||||
pdd lo = -e1 * a1_inv;
|
pdd lo = -e1 * a1_inv;
|
||||||
rational hi_val = mod(lo_val + 1, mod_value);
|
rational hi_val = mod(lo_val + 1, mod_value);
|
||||||
|
@ -425,34 +430,83 @@ namespace polysat {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* -1 <= a*v + b, a odd
|
||||||
|
* forbidden interval for v is [n+1;n[ where n = (-b-1) * a^-1
|
||||||
|
*/
|
||||||
|
bool forbidden_intervals::match_max(
|
||||||
|
signed_constraint const& c,
|
||||||
|
rational const & a1, pdd const& b1, pdd const& e1,
|
||||||
|
rational const & a2, pdd const& b2, pdd const& e2,
|
||||||
|
fi_record& fi) {
|
||||||
|
_last_function = __func__;
|
||||||
|
if (a1.is_zero() && b1.is_max() && a2.is_odd()) {
|
||||||
|
auto& m = e2.manager();
|
||||||
|
rational const& mod_value = m.two_to_N();
|
||||||
|
rational a2_inv;
|
||||||
|
VERIFY(a2.mult_inverse(m.power_of_2(), a2_inv));
|
||||||
|
|
||||||
|
// interval for -1 > a*v + b is [n;n+1[ where n = (-b-1) * a^-1
|
||||||
|
rational lo_val = mod((-1 - b2.val()) * a2_inv, mod_value);
|
||||||
|
pdd lo = (-1 - e2) * a2_inv;
|
||||||
|
rational hi_val = mod(lo_val + 1, mod_value);
|
||||||
|
pdd hi = lo + 1;
|
||||||
|
|
||||||
|
// interval for -1 <= a*v + b is the complement
|
||||||
|
if (c.is_positive()) {
|
||||||
|
std::swap(lo_val, hi_val);
|
||||||
|
std::swap(lo, hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
fi.coeff = 1;
|
||||||
|
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||||
|
// LHS == -1 is a precondition because we can only multiply with a^-1 in equations, not inequalities
|
||||||
|
if (b1 != e1)
|
||||||
|
fi.side_cond.push_back(s.eq(b1, e1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* v > q
|
* v > q
|
||||||
* forbidden interval for v is [0,1[
|
* forbidden interval for v is [0,1[
|
||||||
*
|
*
|
||||||
* v - k > q
|
* v - k > q
|
||||||
* forbidden interval for v is [k,k+1[
|
* forbidden interval for v is [k,k+1[
|
||||||
*
|
*
|
||||||
|
* v > q
|
||||||
|
* forbidden interval for v is [0;q+1[ but at least [0;1[
|
||||||
|
*
|
||||||
|
* The following cases are implemented, and subsume the simple ones above.
|
||||||
|
*
|
||||||
|
* v - k > q
|
||||||
|
* forbidden interval for v is [k;k+q+1[ but at least [k;k+1[
|
||||||
|
*
|
||||||
* a*v - k > q, a odd
|
* a*v - k > q, a odd
|
||||||
* forbidden interval for v is [a^-1*k, a^-1*k + 1[
|
* forbidden interval for v is [a^-1*k, a^-1*k + 1[
|
||||||
*/
|
*/
|
||||||
bool forbidden_intervals::match_non_zero(
|
bool forbidden_intervals::match_non_zero(
|
||||||
signed_constraint const& c,
|
signed_constraint const& c,
|
||||||
rational const & a1, pdd const& b1, pdd const& e1,
|
rational const& a1, pdd const& b1, pdd const& e1,
|
||||||
|
pdd const& q,
|
||||||
fi_record& fi) {
|
fi_record& fi) {
|
||||||
_last_function = __func__;
|
_last_function = __func__;
|
||||||
if (a1.is_odd() && b1.is_zero() && c.is_negative()) {
|
SASSERT(b1.is_val());
|
||||||
|
if (a1.is_one() && c.is_negative()) {
|
||||||
|
// v - k > q
|
||||||
auto& m = e1.manager();
|
auto& m = e1.manager();
|
||||||
rational lo_val(0);
|
rational const& mod_value = m.two_to_N();
|
||||||
auto lo = m.zero();
|
rational lo_val = (-b1).val();
|
||||||
rational hi_val(1);
|
pdd lo = -e1;
|
||||||
auto hi = m.one();
|
rational hi_val = mod(lo_val + 1, mod_value);
|
||||||
|
pdd hi = lo + q + 1;
|
||||||
fi.coeff = 1;
|
fi.coeff = 1;
|
||||||
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||||
if (b1 != e1)
|
|
||||||
fi.side_cond.push_back(s.eq(b1, e1));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (a1.is_odd() && b1.is_val() && c.is_negative()) {
|
if (a1.is_odd() && c.is_negative()) {
|
||||||
|
// a*v - k > q, a odd
|
||||||
auto& m = e1.manager();
|
auto& m = e1.manager();
|
||||||
rational const& mod_value = m.two_to_N();
|
rational const& mod_value = m.two_to_N();
|
||||||
rational a1_inv;
|
rational a1_inv;
|
||||||
|
@ -463,8 +517,6 @@ namespace polysat {
|
||||||
auto hi = lo + 1;
|
auto hi = lo + 1;
|
||||||
fi.coeff = 1;
|
fi.coeff = 1;
|
||||||
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||||
if (b1 != e1)
|
|
||||||
fi.side_cond.push_back(s.eq(b1, e1));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -472,26 +524,45 @@ namespace polysat {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* p > v
|
* p > v
|
||||||
* forbidden interval for v is [-1,0[
|
* forbidden interval for v is [p;0[ but at least [-1,0[
|
||||||
|
*
|
||||||
* p > v + k
|
* p > v + k
|
||||||
* forbidden interval for v is [-k-1,-k[
|
* forbidden interval for v is [p-k;-k[ but at least [-1-k,-k[
|
||||||
|
*
|
||||||
|
* p > a*v + k, a odd
|
||||||
|
* forbidden interval for v is [ a^-1*(-1-k) ; a^-1*(-1-k) + 1 [
|
||||||
*/
|
*/
|
||||||
bool forbidden_intervals::match_non_max(
|
bool forbidden_intervals::match_non_max(
|
||||||
signed_constraint const& c,
|
signed_constraint const& c,
|
||||||
rational const & a2, pdd const& b2, pdd const& e2,
|
pdd const& p,
|
||||||
|
rational const& a2, pdd const& b2, pdd const& e2,
|
||||||
fi_record& fi) {
|
fi_record& fi) {
|
||||||
_last_function = __func__;
|
_last_function = __func__;
|
||||||
if (a2.is_one() && b2.is_val() && c.is_negative()) {
|
SASSERT(b2.is_val());
|
||||||
|
if (a2.is_one() && c.is_negative()) {
|
||||||
|
// p > v + k
|
||||||
auto& m = e2.manager();
|
auto& m = e2.manager();
|
||||||
rational const& mod_value = m.two_to_N();
|
rational const& mod_value = m.two_to_N();
|
||||||
rational lo_val(mod(-b2.val() - 1, mod_value));
|
rational hi_val = (-b2).val();
|
||||||
auto lo = -e2 - 1;
|
pdd hi = -e2;
|
||||||
rational hi_val(mod(lo_val + 1, mod_value));
|
rational lo_val = mod(hi_val - 1, mod_value);
|
||||||
auto hi = -e2;
|
pdd lo = p - e2;
|
||||||
|
fi.coeff = 1;
|
||||||
|
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a2.is_odd() && c.is_negative()) {
|
||||||
|
// p > a*v + k, a odd
|
||||||
|
auto& m = e2.manager();
|
||||||
|
rational const& mod_value = m.two_to_N();
|
||||||
|
rational a2_inv;
|
||||||
|
VERIFY(a2.mult_inverse(m.power_of_2(), a2_inv));
|
||||||
|
rational lo_val = mod(a2_inv * (-1 - b2.val()), mod_value);
|
||||||
|
pdd lo = a2_inv * (-1 - e2);
|
||||||
|
rational hi_val = mod(lo_val + 1, mod_value);
|
||||||
|
pdd hi = lo + 1;
|
||||||
fi.coeff = 1;
|
fi.coeff = 1;
|
||||||
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
fi.interval = eval_interval::proper(lo, lo_val, hi, hi_val);
|
||||||
if (b2 != e2)
|
|
||||||
fi.side_cond.push_back(s.eq(b2, e2));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -73,12 +73,19 @@ namespace polysat {
|
||||||
rational const & a2, pdd const& b2, pdd const& e2,
|
rational const & a2, pdd const& b2, pdd const& e2,
|
||||||
fi_record& fi);
|
fi_record& fi);
|
||||||
|
|
||||||
bool match_non_zero(signed_constraint const& c,
|
bool match_max(signed_constraint const& c,
|
||||||
rational const & a1, pdd const& b1, pdd const& e1,
|
rational const & a1, pdd const& b1, pdd const& e1,
|
||||||
|
rational const & a2, pdd const& b2, pdd const& e2,
|
||||||
|
fi_record& fi);
|
||||||
|
|
||||||
|
bool match_non_zero(signed_constraint const& c,
|
||||||
|
rational const& a1, pdd const& b1, pdd const& e1,
|
||||||
|
pdd const& q,
|
||||||
fi_record& fi);
|
fi_record& fi);
|
||||||
|
|
||||||
bool match_non_max(signed_constraint const& c,
|
bool match_non_max(signed_constraint const& c,
|
||||||
rational const & a2, pdd const& b2, pdd const& e2,
|
pdd const& p,
|
||||||
|
rational const& a2, pdd const& b2, pdd const& e2,
|
||||||
fi_record& fi);
|
fi_record& fi);
|
||||||
|
|
||||||
bool get_interval_ule(signed_constraint const& c, pvar v, fi_record& fi);
|
bool get_interval_ule(signed_constraint const& c, pvar v, fi_record& fi);
|
||||||
|
|
|
@ -50,9 +50,6 @@ static LogLevel get_max_log_level(std::string const& fn, std::string const& pret
|
||||||
(void)fn;
|
(void)fn;
|
||||||
(void)pretty_fn;
|
(void)pretty_fn;
|
||||||
|
|
||||||
if (fn == "push_cjust")
|
|
||||||
return LogLevel::Verbose;
|
|
||||||
|
|
||||||
// if (fn == "pop_levels")
|
// if (fn == "pop_levels")
|
||||||
// return LogLevel::Default;
|
// return LogLevel::Default;
|
||||||
|
|
||||||
|
@ -65,9 +62,11 @@ static LogLevel get_max_log_level(std::string const& fn, std::string const& pret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter log messages
|
/// Filter log messages
|
||||||
bool polysat_should_log(LogLevel msg_level, std::string fn, std::string pretty_fn) {
|
bool polysat_should_log(unsigned verbose_lvl, LogLevel msg_level, std::string fn, std::string pretty_fn) {
|
||||||
if (!g_log_enabled)
|
if (!g_log_enabled)
|
||||||
return false;
|
return false;
|
||||||
|
if (get_verbosity_level() < verbose_lvl)
|
||||||
|
return false;
|
||||||
LogLevel max_log_level = get_max_log_level(fn, pretty_fn);
|
LogLevel max_log_level = get_max_log_level(fn, pretty_fn);
|
||||||
return msg_level <= max_log_level;
|
return msg_level <= max_log_level;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,12 +56,11 @@ enum class LogLevel : int {
|
||||||
Heading2 = 2,
|
Heading2 = 2,
|
||||||
Heading3 = 3,
|
Heading3 = 3,
|
||||||
Default = 4,
|
Default = 4,
|
||||||
Verbose = 5,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Filter log messages
|
/// Filter log messages
|
||||||
bool
|
bool
|
||||||
polysat_should_log(LogLevel msg_level, std::string fn, std::string pretty_fn);
|
polysat_should_log(unsigned verbose_lvl, LogLevel msg_level, std::string fn, std::string pretty_fn);
|
||||||
|
|
||||||
std::pair<std::ostream&, bool>
|
std::pair<std::ostream&, bool>
|
||||||
polysat_log(LogLevel msg_level, std::string fn, std::string pretty_fn);
|
polysat_log(LogLevel msg_level, std::string fn, std::string pretty_fn);
|
||||||
|
@ -70,31 +69,36 @@ polysat_log(LogLevel msg_level, std::string fn, std::string pretty_fn);
|
||||||
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG_(lvl, x) \
|
#define LOG_(verbose_lvl, log_lvl, x) \
|
||||||
do { \
|
do { \
|
||||||
if (polysat_should_log(lvl, __func__, __PRETTY_FUNCTION__)) { \
|
if (polysat_should_log(verbose_lvl, log_lvl, __func__, __PRETTY_FUNCTION__)) { \
|
||||||
auto pair = polysat_log(lvl, __func__, __PRETTY_FUNCTION__); \
|
auto pair = polysat_log(log_lvl, __func__, __PRETTY_FUNCTION__); \
|
||||||
std::ostream& os = pair.first; \
|
std::ostream& os = pair.first; \
|
||||||
bool should_reset = pair.second; \
|
bool should_reset = pair.second; \
|
||||||
os << x; \
|
os << x; \
|
||||||
if (should_reset) \
|
if (should_reset) \
|
||||||
os << color_reset(); \
|
os << color_reset(); \
|
||||||
os << std::endl; \
|
os << std::endl; \
|
||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define LOG_CONCAT_HELPER(a,b) a ## b
|
#define LOG_CONCAT_HELPER(a,b) a ## b
|
||||||
#define LOG_CONCAT(a,b) LOG_CONCAT_HELPER(a,b)
|
#define LOG_CONCAT(a,b) LOG_CONCAT_HELPER(a,b)
|
||||||
|
|
||||||
#define LOG_INDENT(lvl, x) \
|
#define LOG_INDENT(verbose_lvl, log_lvl, x) \
|
||||||
LOG_(lvl, x); \
|
LOG_(verbose_lvl, log_lvl, x); \
|
||||||
polysat_log_indent LOG_CONCAT(polysat_log_indent_obj_, __LINE__) (4);
|
polysat_log_indent LOG_CONCAT(polysat_log_indent_obj_, __LINE__) (4);
|
||||||
|
|
||||||
#define LOG_H1(x) LOG_INDENT(LogLevel::Heading1, x)
|
#define LOG_H1(x) LOG_INDENT(0, LogLevel::Heading1, x)
|
||||||
#define LOG_H2(x) LOG_INDENT(LogLevel::Heading2, x)
|
#define LOG_H2(x) LOG_INDENT(0, LogLevel::Heading2, x)
|
||||||
#define LOG_H3(x) LOG_INDENT(LogLevel::Heading3, x)
|
#define LOG_H3(x) LOG_INDENT(0, LogLevel::Heading3, x)
|
||||||
#define LOG(x) LOG_(LogLevel::Default , x)
|
#define LOG(x) LOG_(0, LogLevel::Default , x)
|
||||||
#define LOG_V(x) LOG_(LogLevel::Verbose , x)
|
|
||||||
|
#define LOG_H1_V(verbose_lvl, x) LOG_INDENT(verbose_lvl, LogLevel::Heading1, x)
|
||||||
|
#define LOG_H2_V(verbose_lvl, x) LOG_INDENT(verbose_lvl, LogLevel::Heading2, x)
|
||||||
|
#define LOG_H3_V(verbose_lvl, x) LOG_INDENT(verbose_lvl, LogLevel::Heading3, x)
|
||||||
|
#define LOG_V(verbose_lvl, x) LOG_(verbose_lvl, LogLevel::Default , x)
|
||||||
|
|
||||||
#define COND_LOG(c, x) if (c) LOG(x)
|
#define COND_LOG(c, x) if (c) LOG(x)
|
||||||
#define LOGE(x) LOG(#x << " = " << (x))
|
#define LOGE(x) LOG(#x << " = " << (x))
|
||||||
|
|
||||||
|
@ -110,18 +114,23 @@ inline void set_log_enabled(bool) {}
|
||||||
inline bool get_log_enabled() { return false; }
|
inline bool get_log_enabled() { return false; }
|
||||||
class scoped_set_log_enabled {};
|
class scoped_set_log_enabled {};
|
||||||
|
|
||||||
#define LOG_(lvl, x) \
|
#define LOG_(vlvl, lvl, x) \
|
||||||
do { \
|
do { \
|
||||||
/* do nothing */ \
|
/* do nothing */ \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define LOG_H1(x) LOG_(0, x)
|
#define LOG_H1(x) LOG_(0, 0, x)
|
||||||
#define LOG_H2(x) LOG_(0, x)
|
#define LOG_H2(x) LOG_(0, 0, x)
|
||||||
#define LOG_H3(x) LOG_(0, x)
|
#define LOG_H3(x) LOG_(0, 0, x)
|
||||||
#define LOG(x) LOG_(0, x)
|
#define LOG(x) LOG_(0, 0, x)
|
||||||
#define LOG_V(x) LOG_(0, x)
|
|
||||||
#define COND_LOG(c, x) LOG_(c, x)
|
#define LOG_H1_V(v, x) LOG_(v, 0, x)
|
||||||
#define LOGE(x) LOG_(0, x)
|
#define LOG_H2_V(v, x) LOG_(v, 0, x)
|
||||||
|
#define LOG_H3_V(v, x) LOG_(v, 0, x)
|
||||||
|
#define LOG_V(v, x) LOG_(v, 0, x)
|
||||||
|
|
||||||
|
#define COND_LOG(c, x) LOG_(0, c, x)
|
||||||
|
#define LOGE(x) LOG_(0, 0, x)
|
||||||
|
|
||||||
#define IF_LOGGING(x) \
|
#define IF_LOGGING(x) \
|
||||||
do { \
|
do { \
|
||||||
|
|
30
src/math/polysat/number.h
Normal file
30
src/math/polysat/number.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2021 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
polysat numbers
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Nikolaj Bjorner (nbjorner) 2021-03-19
|
||||||
|
Jakob Rath 2021-04-06
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#pragma once
|
||||||
|
#include "math/polysat/types.h"
|
||||||
|
|
||||||
|
namespace polysat {
|
||||||
|
|
||||||
|
inline unsigned get_parity(rational const& val, unsigned num_bits) {
|
||||||
|
if (val.is_zero())
|
||||||
|
return num_bits;
|
||||||
|
return val.trailing_zeros();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Return val with the lower k bits set to zero. */
|
||||||
|
inline rational clear_lower_bits(rational const& val, unsigned k) {
|
||||||
|
return val - mod(val, rational::power_of_two(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,8 +25,8 @@ Additional possible functionality on constraints:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
op_constraint::op_constraint(constraint_manager& m, code c, pdd const& p, pdd const& q, pdd const& r) :
|
op_constraint::op_constraint(code c, pdd const& p, pdd const& q, pdd const& r) :
|
||||||
constraint(m, ckind_t::op_t), m_op(c), m_p(p), m_q(q), m_r(r) {
|
constraint(ckind_t::op_t), m_op(c), m_p(p), m_q(q), m_r(r) {
|
||||||
m_vars.append(p.free_vars());
|
m_vars.append(p.free_vars());
|
||||||
for (auto v : q.free_vars())
|
for (auto v : q.free_vars())
|
||||||
if (!m_vars.contains(v))
|
if (!m_vars.contains(v))
|
||||||
|
@ -570,20 +570,26 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void op_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
void op_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
||||||
auto p_coeff = s.subst(p()).get_univariate_coefficients();
|
pdd pv = s.subst(p());
|
||||||
auto q_coeff = s.subst(q()).get_univariate_coefficients();
|
if (!pv.is_univariate_in(v))
|
||||||
auto r_coeff = s.subst(r()).get_univariate_coefficients();
|
return;
|
||||||
|
pdd qv = s.subst(q());
|
||||||
|
if (!qv.is_univariate_in(v))
|
||||||
|
return;
|
||||||
|
pdd rv = s.subst(r());
|
||||||
|
if (!rv.is_univariate_in(v))
|
||||||
|
return;
|
||||||
switch (m_op) {
|
switch (m_op) {
|
||||||
case code::lshr_op:
|
case code::lshr_op:
|
||||||
us.add_lshr(p_coeff, q_coeff, r_coeff, !is_positive, dep);
|
us.add_lshr(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
|
||||||
break;
|
break;
|
||||||
case code::shl_op:
|
case code::shl_op:
|
||||||
us.add_shl(p_coeff, q_coeff, r_coeff, !is_positive, dep);
|
us.add_shl(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
|
||||||
break;
|
break;
|
||||||
case code::and_op:
|
case code::and_op:
|
||||||
us.add_and(p_coeff, q_coeff, r_coeff, !is_positive, dep);
|
us.add_and(pv.get_univariate_coefficients(), qv.get_univariate_coefficients(), rv.get_univariate_coefficients(), !is_positive, dep);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOT_IMPLEMENTED_YET();
|
NOT_IMPLEMENTED_YET();
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace polysat {
|
||||||
pdd m_q; // operand2
|
pdd m_q; // operand2
|
||||||
pdd m_r; // result
|
pdd m_r; // result
|
||||||
|
|
||||||
op_constraint(constraint_manager& m, code c, pdd const& p, pdd const& q, pdd const& r);
|
op_constraint(code c, pdd const& p, pdd const& q, pdd const& r);
|
||||||
lbool eval(pdd const& p, pdd const& q, pdd const& r) const;
|
lbool eval(pdd const& p, pdd const& q, pdd const& r) const;
|
||||||
clause_ref produce_lemma(solver& s, assignment const& a);
|
clause_ref produce_lemma(solver& s, assignment const& a);
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ namespace polysat {
|
||||||
bool operator==(constraint const& other) const override;
|
bool operator==(constraint const& other) const override;
|
||||||
bool is_eq() const override { return false; }
|
bool is_eq() const override { return false; }
|
||||||
|
|
||||||
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct op_constraint_args {
|
struct op_constraint_args {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,9 +31,11 @@ namespace polysat {
|
||||||
bool is_non_overflow(pdd const& x, pdd const& y, signed_constraint& c);
|
bool is_non_overflow(pdd const& x, pdd const& y, signed_constraint& c);
|
||||||
signed_constraint ineq(bool strict, pdd const& lhs, pdd const& rhs);
|
signed_constraint ineq(bool strict, pdd const& lhs, pdd const& rhs);
|
||||||
|
|
||||||
bool propagate(conflict& core, inequality const& crit1, signed_constraint c);
|
void log_lemma(pvar v, conflict& core);
|
||||||
bool add_conflict(conflict& core, inequality const& crit1, signed_constraint c);
|
bool propagate(pvar v, conflict& core, signed_constraint const& crit1, signed_constraint c);
|
||||||
bool add_conflict(conflict& core, inequality const& crit1, inequality const& crit2, signed_constraint c);
|
bool propagate(pvar v, conflict& core, inequality const& crit1, signed_constraint c);
|
||||||
|
bool add_conflict(pvar v, conflict& core, inequality const& crit1, signed_constraint c);
|
||||||
|
bool add_conflict(pvar v, conflict& core, inequality const& crit1, inequality const& crit2, signed_constraint c);
|
||||||
|
|
||||||
bool try_ugt_x(pvar v, conflict& core, inequality const& c);
|
bool try_ugt_x(pvar v, conflict& core, inequality const& c);
|
||||||
|
|
||||||
|
@ -49,10 +51,16 @@ namespace polysat {
|
||||||
bool try_parity(pvar x, conflict& core, inequality const& axb_l_y);
|
bool try_parity(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
bool try_parity_diseq(pvar x, conflict& core, inequality const& axb_l_y);
|
bool try_parity_diseq(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
bool try_mul_bounds(pvar x, conflict& core, inequality const& axb_l_y);
|
bool try_mul_bounds(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
bool try_factor_equality(pvar x, conflict& core, inequality const& a_l_b);
|
bool try_factor_equality1(pvar x, conflict& core, inequality const& a_l_b);
|
||||||
|
bool try_factor_equality2(pvar x, conflict& core, inequality const& a_l_b);
|
||||||
|
bool try_infer_equality(pvar x, conflict& core, inequality const& a_l_b);
|
||||||
bool try_mul_eq_1(pvar x, conflict& core, inequality const& axb_l_y);
|
bool try_mul_eq_1(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
bool try_mul_odd(pvar x, conflict& core, inequality const& axb_l_y);
|
bool try_mul_odd(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
|
bool try_mul_eq_bound(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
|
bool try_transitivity(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
bool try_tangent(pvar v, conflict& core, inequality const& c);
|
bool try_tangent(pvar v, conflict& core, inequality const& c);
|
||||||
|
bool try_add_overflow_bound(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
|
bool try_add_mul_bound(pvar x, conflict& core, inequality const& axb_l_y);
|
||||||
|
|
||||||
// c := lhs ~ v
|
// c := lhs ~ v
|
||||||
// where ~ is < or <=
|
// where ~ is < or <=
|
||||||
|
@ -62,7 +70,10 @@ namespace polysat {
|
||||||
bool is_g_v(pvar v, inequality const& c);
|
bool is_g_v(pvar v, inequality const& c);
|
||||||
|
|
||||||
// c := x ~ Y
|
// c := x ~ Y
|
||||||
bool is_x_l_Y(pvar x, inequality const& c, pdd& y);
|
bool is_x_l_Y(pvar x, inequality const& i, pdd& y);
|
||||||
|
|
||||||
|
// c := Y ~ x
|
||||||
|
bool is_Y_l_x(pvar x, inequality const& i, pdd& y);
|
||||||
|
|
||||||
// c := X*y ~ X*Z
|
// c := X*y ~ X*Z
|
||||||
bool is_Xy_l_XZ(pvar y, inequality const& c, pdd& x, pdd& z);
|
bool is_Xy_l_XZ(pvar y, inequality const& c, pdd& x, pdd& z);
|
||||||
|
@ -107,6 +118,17 @@ namespace polysat {
|
||||||
// p := coeff*x*y where coeff_x = coeff*x, x a variable
|
// p := coeff*x*y where coeff_x = coeff*x, x a variable
|
||||||
bool is_coeffxY(pdd const& coeff_x, pdd const& p, pdd& y);
|
bool is_coeffxY(pdd const& coeff_x, pdd const& p, pdd& y);
|
||||||
|
|
||||||
|
// i := x + y >= x or x + y > x
|
||||||
|
bool is_add_overflow(pvar x, inequality const& i, pdd& y);
|
||||||
|
|
||||||
|
bool has_upper_bound(pvar x, conflict& core, rational& bound, vector<signed_constraint>& x_ge_bound);
|
||||||
|
|
||||||
|
bool has_lower_bound(pvar x, conflict& core, rational& bound, vector<signed_constraint>& x_le_bound);
|
||||||
|
|
||||||
|
// determine min/max parity of polynomial
|
||||||
|
unsigned min_parity(pdd const& p);
|
||||||
|
unsigned max_parity(pdd const& p);
|
||||||
|
|
||||||
bool is_forced_eq(pdd const& p, rational const& val);
|
bool is_forced_eq(pdd const& p, rational const& val);
|
||||||
bool is_forced_eq(pdd const& p, int i) { return is_forced_eq(p, rational(i)); }
|
bool is_forced_eq(pdd const& p, int i) { return is_forced_eq(p, rational(i)); }
|
||||||
|
|
||||||
|
@ -118,6 +140,13 @@ namespace polysat {
|
||||||
|
|
||||||
bool is_forced_true(signed_constraint const& sc);
|
bool is_forced_true(signed_constraint const& sc);
|
||||||
|
|
||||||
|
bool try_inequality(pvar v, inequality const& i, conflict& core);
|
||||||
|
|
||||||
|
bool try_umul_ovfl(pvar v, signed_constraint const& c, conflict& core);
|
||||||
|
bool try_umul_noovfl_lo(pvar v, signed_constraint const& c, conflict& core);
|
||||||
|
bool try_umul_noovfl_bounds(pvar v, signed_constraint const& c, conflict& core);
|
||||||
|
bool try_umul_ovfl_bounds(pvar v, signed_constraint const& c, conflict& core);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
saturation(solver& s);
|
saturation(solver& s);
|
||||||
void perform(pvar v, conflict& core);
|
void perform(pvar v, conflict& core);
|
||||||
|
|
|
@ -77,13 +77,85 @@ namespace polysat {
|
||||||
|
|
||||||
bool simplify_clause::apply(clause& cl) {
|
bool simplify_clause::apply(clause& cl) {
|
||||||
LOG_H1("Simplifying clause: " << cl);
|
LOG_H1("Simplifying clause: " << cl);
|
||||||
|
bool simplified = false;
|
||||||
|
if (try_remove_equations(cl))
|
||||||
|
simplified = true;
|
||||||
#if 0
|
#if 0
|
||||||
if (try_recognize_bailout(cl))
|
if (try_recognize_bailout(cl))
|
||||||
return true;
|
simplified = true;
|
||||||
#endif
|
#endif
|
||||||
if (try_equal_body_subsumptions(cl))
|
if (try_equal_body_subsumptions(cl))
|
||||||
return true;
|
simplified = true;
|
||||||
return false;
|
return simplified;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we have:
|
||||||
|
* p <= q
|
||||||
|
* p - q == 0
|
||||||
|
* Then remove the equality.
|
||||||
|
*
|
||||||
|
* If we have:
|
||||||
|
* p < q
|
||||||
|
* p - q == 0
|
||||||
|
* Then merge into p <= q.
|
||||||
|
*/
|
||||||
|
bool simplify_clause::try_remove_equations(clause& cl) {
|
||||||
|
LOG_H2("Remove superfluous equations from: " << cl);
|
||||||
|
bool const has_eqn = any_of(cl, [&](sat::literal lit) { return s.lit2cnstr(lit).is_eq(); });
|
||||||
|
if (!has_eqn)
|
||||||
|
return false;
|
||||||
|
bool any_removed = false;
|
||||||
|
bool_vector& should_remove = m_bools;
|
||||||
|
should_remove.fill(cl.size(), false);
|
||||||
|
for (unsigned i = cl.size(); i-- > 0; ) {
|
||||||
|
sat::literal const lit = cl[i];
|
||||||
|
signed_constraint const c = s.lit2cnstr(lit);
|
||||||
|
if (!c->is_ule())
|
||||||
|
continue;
|
||||||
|
if (c->is_eq())
|
||||||
|
continue;
|
||||||
|
#if 1
|
||||||
|
// Disable the case p<q && p=q for now.
|
||||||
|
// The merging of less-than and equality may remove premises from the lemma.
|
||||||
|
// See test_band5.
|
||||||
|
// TODO: fix and re-enable
|
||||||
|
if (c.is_negative())
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
LOG_V(10, "Examine: " << lit_pp(s, lit));
|
||||||
|
pdd const p = c->to_ule().lhs();
|
||||||
|
pdd const q = c->to_ule().rhs();
|
||||||
|
signed_constraint const eq = s.m_constraints.find_eq(p - q);
|
||||||
|
if (!eq)
|
||||||
|
continue;
|
||||||
|
auto const eq_it = std::find(cl.begin(), cl.end(), eq.blit());
|
||||||
|
if (eq_it == cl.end())
|
||||||
|
continue;
|
||||||
|
unsigned const eq_idx = std::distance(cl.begin(), eq_it);
|
||||||
|
any_removed = true;
|
||||||
|
should_remove[eq_idx] = true;
|
||||||
|
if (c.is_positive()) {
|
||||||
|
// c: p <= q
|
||||||
|
// eq: p == q
|
||||||
|
LOG("Removing " << eq.blit() << ": " << eq << " because it subsumes " << cl[i] << ": " << s.lit2cnstr(cl[i]));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// c: p > q
|
||||||
|
// eq: p == q
|
||||||
|
cl[i] = s.ule(q, p).blit();
|
||||||
|
LOG("Merge " << eq.blit() << ": " << eq << " and " << lit << ": " << c << " to obtain " << cl[i] << ": " << s.lit2cnstr(cl[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove superfluous equations
|
||||||
|
if (!any_removed)
|
||||||
|
return false;
|
||||||
|
unsigned j = 0;
|
||||||
|
for (unsigned i = 0; i < cl.size(); ++i)
|
||||||
|
if (!should_remove[i])
|
||||||
|
cl[j++] = cl[i];
|
||||||
|
cl.m_literals.shrink(j);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If x != k appears among the new literals, all others are superfluous.
|
// If x != k appears among the new literals, all others are superfluous.
|
||||||
|
@ -94,7 +166,7 @@ namespace polysat {
|
||||||
sat::literal eq = sat::null_literal;
|
sat::literal eq = sat::null_literal;
|
||||||
rational k;
|
rational k;
|
||||||
for (sat::literal lit : cl) {
|
for (sat::literal lit : cl) {
|
||||||
LOG_V("Examine " << lit_pp(s, lit));
|
LOG_V(10, "Examine " << lit_pp(s, lit));
|
||||||
lbool status = s.m_bvars.value(lit);
|
lbool status = s.m_bvars.value(lit);
|
||||||
// skip premise literals
|
// skip premise literals
|
||||||
if (status == l_false)
|
if (status == l_false)
|
||||||
|
@ -237,7 +309,7 @@ namespace polysat {
|
||||||
for (unsigned i = 0; i < cl.size(); ++i) {
|
for (unsigned i = 0; i < cl.size(); ++i) {
|
||||||
subs_entry& entry = m_entries[i];
|
subs_entry& entry = m_entries[i];
|
||||||
sat::literal lit = cl[i];
|
sat::literal lit = cl[i];
|
||||||
LOG("Literal " << lit_pp(s, lit));
|
LOG_V(10, "Literal " << lit_pp(s, lit));
|
||||||
signed_constraint c = s.lit2cnstr(lit);
|
signed_constraint c = s.lit2cnstr(lit);
|
||||||
prepare_subs_entry(entry, c);
|
prepare_subs_entry(entry, c);
|
||||||
}
|
}
|
||||||
|
@ -254,7 +326,7 @@ namespace polysat {
|
||||||
continue;
|
continue;
|
||||||
if (e.interval.currently_contains(f.interval)) {
|
if (e.interval.currently_contains(f.interval)) {
|
||||||
// f subset of e ==> f.src subsumed by e.src
|
// f subset of e ==> f.src subsumed by e.src
|
||||||
LOG("Removing " << s.lit2cnstr(cl[i]) << " because it subsumes " << s.lit2cnstr(cl[j]));
|
LOG("Removing " << cl[i] << ": " << s.lit2cnstr(cl[i]) << " because it subsumes " << cl[j] << ": " << s.lit2cnstr(cl[j]));
|
||||||
e.subsuming = true;
|
e.subsuming = true;
|
||||||
any_subsumed = true;
|
any_subsumed = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -28,7 +28,9 @@ namespace polysat {
|
||||||
|
|
||||||
solver& s;
|
solver& s;
|
||||||
vector<subs_entry> m_entries;
|
vector<subs_entry> m_entries;
|
||||||
|
bool_vector m_bools;
|
||||||
|
|
||||||
|
bool try_remove_equations(clause& cl);
|
||||||
bool try_recognize_bailout(clause& cl);
|
bool try_recognize_bailout(clause& cl);
|
||||||
bool try_equal_body_subsumptions(clause& cl);
|
bool try_equal_body_subsumptions(clause& cl);
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
smul_fl_constraint::smul_fl_constraint(constraint_manager& m, pdd const& p, pdd const& q, bool is_overflow):
|
smul_fl_constraint::smul_fl_constraint(pdd const& p, pdd const& q, bool is_overflow):
|
||||||
constraint(m, ckind_t::smul_fl_t), m_is_overflow(is_overflow), m_p(p), m_q(q) {
|
constraint(ckind_t::smul_fl_t), m_is_overflow(is_overflow), m_p(p), m_q(q) {
|
||||||
simplify();
|
simplify();
|
||||||
m_vars.append(m_p.free_vars());
|
m_vars.append(m_p.free_vars());
|
||||||
for (auto v : m_q.free_vars())
|
for (auto v : m_q.free_vars())
|
||||||
|
@ -119,13 +119,17 @@ namespace polysat {
|
||||||
&& q() == other.to_smul_fl().q();
|
&& q() == other.to_smul_fl().q();
|
||||||
}
|
}
|
||||||
|
|
||||||
void smul_fl_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
void smul_fl_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
||||||
auto p_coeff = s.subst(p()).get_univariate_coefficients();
|
auto p1 = s.subst(p());
|
||||||
auto q_coeff = s.subst(q()).get_univariate_coefficients();
|
if (!p1.is_univariate_in(v))
|
||||||
|
return;
|
||||||
|
auto q1 = s.subst(q());
|
||||||
|
if (!q1.is_univariate_in(v))
|
||||||
|
return;
|
||||||
if (is_overflow())
|
if (is_overflow())
|
||||||
us.add_smul_ovfl(p_coeff, q_coeff, !is_positive, dep);
|
us.add_smul_ovfl(p1.get_univariate_coefficients(), q1.get_univariate_coefficients(), !is_positive, dep);
|
||||||
else
|
else
|
||||||
us.add_smul_udfl(p_coeff, q_coeff, !is_positive, dep);
|
us.add_smul_udfl(p1.get_univariate_coefficients(), q1.get_univariate_coefficients(), !is_positive, dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace polysat {
|
||||||
pdd m_q;
|
pdd m_q;
|
||||||
|
|
||||||
void simplify();
|
void simplify();
|
||||||
smul_fl_constraint(constraint_manager& m, pdd const& p, pdd const& q, bool is_overflow);
|
smul_fl_constraint(pdd const& p, pdd const& q, bool is_overflow);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~smul_fl_constraint() override {}
|
~smul_fl_constraint() override {}
|
||||||
|
@ -42,7 +42,7 @@ namespace polysat {
|
||||||
bool operator==(constraint const& other) const override;
|
bool operator==(constraint const& other) const override;
|
||||||
bool is_eq() const override { return false; }
|
bool is_eq() const override { return false; }
|
||||||
|
|
||||||
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ namespace polysat {
|
||||||
m_config.m_max_conflicts = m_params.get_uint("max_conflicts", UINT_MAX);
|
m_config.m_max_conflicts = m_params.get_uint("max_conflicts", UINT_MAX);
|
||||||
m_config.m_max_decisions = m_params.get_uint("max_decisions", UINT_MAX);
|
m_config.m_max_decisions = m_params.get_uint("max_decisions", UINT_MAX);
|
||||||
m_config.m_log_conflicts = pp.log_conflicts();
|
m_config.m_log_conflicts = pp.log_conflicts();
|
||||||
|
m_rand.set_seed(m_params.get_uint("random_seed", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool solver::should_search() {
|
bool solver::should_search() {
|
||||||
|
@ -75,6 +76,7 @@ namespace polysat {
|
||||||
LOG("Assignment: " << assignments_pp(*this));
|
LOG("Assignment: " << assignments_pp(*this));
|
||||||
if (is_conflict()) LOG("Conflict: " << m_conflict);
|
if (is_conflict()) LOG("Conflict: " << m_conflict);
|
||||||
IF_LOGGING(m_viable.log());
|
IF_LOGGING(m_viable.log());
|
||||||
|
SASSERT(var_queue_invariant());
|
||||||
if (is_conflict() && at_base_level()) { LOG_H2("UNSAT"); return l_false; }
|
if (is_conflict() && at_base_level()) { LOG_H2("UNSAT"); return l_false; }
|
||||||
else if (is_conflict()) resolve_conflict();
|
else if (is_conflict()) resolve_conflict();
|
||||||
else if (should_add_pwatch()) add_pwatch();
|
else if (should_add_pwatch()) add_pwatch();
|
||||||
|
@ -241,9 +243,11 @@ namespace polysat {
|
||||||
for (; i < sz && !is_conflict(); ++i)
|
for (; i < sz && !is_conflict(); ++i)
|
||||||
if (!propagate(v, wlist[i]))
|
if (!propagate(v, wlist[i]))
|
||||||
wlist[j++] = wlist[i];
|
wlist[j++] = wlist[i];
|
||||||
for (; i < sz; ++i)
|
for (; i < sz; ++i)
|
||||||
wlist[j++] = wlist[i];
|
wlist[j++] = wlist[i];
|
||||||
wlist.shrink(j);
|
wlist.shrink(j);
|
||||||
|
if (is_conflict())
|
||||||
|
shuffle(wlist.size(), wlist.data(), m_rand);
|
||||||
DEBUG_CODE(m_locked_wlist = std::nullopt;);
|
DEBUG_CODE(m_locked_wlist = std::nullopt;);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +398,7 @@ namespace polysat {
|
||||||
|
|
||||||
void solver::add_pwatch(constraint* c, pvar v) {
|
void solver::add_pwatch(constraint* c, pvar v) {
|
||||||
SASSERT(m_locked_wlist != v); // the propagate loop will not discover the new size
|
SASSERT(m_locked_wlist != v); // the propagate loop will not discover the new size
|
||||||
LOG_V("Watching v" << v << " in constraint " << show_deref(c));
|
LOG_V(20, "Watching v" << v << " in constraint " << show_deref(c));
|
||||||
m_pwatch[v].push_back(c);
|
m_pwatch[v].push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,7 +499,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
case trail_instr_t::assign_i: {
|
case trail_instr_t::assign_i: {
|
||||||
auto v = m_search.back().var();
|
auto v = m_search.back().var();
|
||||||
LOG_V("Undo assign_i: v" << v);
|
LOG_V(20, "Undo assign_i: v" << v);
|
||||||
unsigned active_level = get_level(v);
|
unsigned active_level = get_level(v);
|
||||||
|
|
||||||
if (active_level <= target_level) {
|
if (active_level <= target_level) {
|
||||||
|
@ -511,7 +515,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
case trail_instr_t::assign_bool_i: {
|
case trail_instr_t::assign_bool_i: {
|
||||||
sat::literal lit = m_search.back().lit();
|
sat::literal lit = m_search.back().lit();
|
||||||
LOG_V("Undo assign_bool_i: " << lit);
|
LOG_V(20, "Undo assign_bool_i: " << lit);
|
||||||
unsigned active_level = m_bvars.level(lit);
|
unsigned active_level = m_bvars.level(lit);
|
||||||
|
|
||||||
if (active_level <= target_level)
|
if (active_level <= target_level)
|
||||||
|
@ -588,15 +592,13 @@ namespace polysat {
|
||||||
// Simple hack to try deciding the boolean skeleton first
|
// Simple hack to try deciding the boolean skeleton first
|
||||||
if (!can_bdecide()) {
|
if (!can_bdecide()) {
|
||||||
// enqueue all not-yet-true clauses
|
// enqueue all not-yet-true clauses
|
||||||
for (auto const& cls : m_constraints.clauses()) {
|
for (clause const& cl : m_constraints.clauses()) {
|
||||||
for (auto const& cl : cls) {
|
bool is_true = any_of(cl, [&](sat::literal lit) { return m_bvars.is_true(lit); });
|
||||||
bool is_true = any_of(*cl, [&](sat::literal lit) { return m_bvars.is_true(lit); });
|
if (is_true)
|
||||||
if (is_true)
|
continue;
|
||||||
continue;
|
size_t undefs = count_if(cl, [&](sat::literal lit) { return !m_bvars.is_assigned(lit); });
|
||||||
size_t undefs = count_if(*cl, [&](sat::literal lit) { return !m_bvars.is_assigned(lit); });
|
if (undefs >= 2)
|
||||||
if (undefs >= 2)
|
m_lemmas.push_back(&cl);
|
||||||
m_lemmas.push_back(cl.get());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -608,7 +610,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::bdecide() {
|
void solver::bdecide() {
|
||||||
clause& lemma = *m_lemmas[m_lemmas_qhead++];
|
clause const& lemma = *m_lemmas[m_lemmas_qhead++];
|
||||||
on_scope_exit update_trail = [this]() {
|
on_scope_exit update_trail = [this]() {
|
||||||
// must be done after push_level, but also if we return early.
|
// must be done after push_level, but also if we return early.
|
||||||
m_trail.push_back(trail_instr_t::lemma_qhead_i);
|
m_trail.push_back(trail_instr_t::lemma_qhead_i);
|
||||||
|
@ -654,7 +656,7 @@ namespace polysat {
|
||||||
// (fail here in debug mode so we notice if we miss some)
|
// (fail here in debug mode so we notice if we miss some)
|
||||||
DEBUG_CODE( UNREACHABLE(); );
|
DEBUG_CODE( UNREACHABLE(); );
|
||||||
m_free_pvars.unassign_var_eh(v);
|
m_free_pvars.unassign_var_eh(v);
|
||||||
set_conflict(v, false);
|
SASSERT(is_conflict());
|
||||||
return;
|
return;
|
||||||
case find_t::singleton:
|
case find_t::singleton:
|
||||||
// NOTE: this case may happen legitimately if all other possibilities were excluded by brute force search
|
// NOTE: this case may happen legitimately if all other possibilities were excluded by brute force search
|
||||||
|
@ -734,9 +736,16 @@ namespace polysat {
|
||||||
SASSERT(!m_search.assignment().contains(v));
|
SASSERT(!m_search.assignment().contains(v));
|
||||||
if (lemma) {
|
if (lemma) {
|
||||||
add_clause(*lemma);
|
add_clause(*lemma);
|
||||||
SASSERT(!is_conflict()); // if we have a conflict here, we could have produced this lemma already earlier
|
if (is_conflict()) {
|
||||||
if (can_propagate())
|
// If we have a conflict here, we should have produced this lemma already earlier
|
||||||
|
LOG("Conflict after constraint::produce_lemma: TODO: should have found this lemma earlier");
|
||||||
|
m_free_pvars.unassign_var_eh(v);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
if (can_propagate()) {
|
||||||
|
m_free_pvars.unassign_var_eh(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (c) {
|
if (c) {
|
||||||
LOG_H2("Chosen assignment " << assignment_pp(*this, v, val) << " is not actually viable!");
|
LOG_H2("Chosen assignment " << assignment_pp(*this, v, val) << " is not actually viable!");
|
||||||
|
@ -753,10 +762,11 @@ namespace polysat {
|
||||||
case find_t::empty:
|
case find_t::empty:
|
||||||
LOG("Fallback solver: unsat");
|
LOG("Fallback solver: unsat");
|
||||||
m_free_pvars.unassign_var_eh(v);
|
m_free_pvars.unassign_var_eh(v);
|
||||||
set_conflict(v, true);
|
SASSERT(is_conflict());
|
||||||
return;
|
return;
|
||||||
case find_t::resource_out:
|
case find_t::resource_out:
|
||||||
UNREACHABLE(); // TODO: abort solving
|
UNREACHABLE(); // TODO: abort solving
|
||||||
|
m_free_pvars.unassign_var_eh(v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -938,7 +948,6 @@ namespace polysat {
|
||||||
clause* best_lemma = nullptr;
|
clause* best_lemma = nullptr;
|
||||||
|
|
||||||
auto appraise_lemma = [&](clause* lemma) {
|
auto appraise_lemma = [&](clause* lemma) {
|
||||||
m_simplify_clause.apply(*lemma);
|
|
||||||
auto score = compute_lemma_score(*lemma);
|
auto score = compute_lemma_score(*lemma);
|
||||||
if (score)
|
if (score)
|
||||||
LOG(" score: " << *score);
|
LOG(" score: " << *score);
|
||||||
|
@ -958,14 +967,14 @@ namespace polysat {
|
||||||
appraise_lemma(lemmas.back());
|
appraise_lemma(lemmas.back());
|
||||||
}
|
}
|
||||||
SASSERT(best_score < lemma_score::max());
|
SASSERT(best_score < lemma_score::max());
|
||||||
SASSERT(best_lemma);
|
VERIFY(best_lemma);
|
||||||
|
|
||||||
unsigned const jump_level = std::max(best_score.jump_level(), base_level());
|
unsigned const jump_level = std::max(best_score.jump_level(), base_level());
|
||||||
SASSERT(jump_level <= max_jump_level);
|
SASSERT(jump_level <= max_jump_level);
|
||||||
|
|
||||||
LOG("best_score: " << best_score);
|
LOG("best_score: " << best_score);
|
||||||
LOG("best_lemma: " << *best_lemma);
|
LOG("best_lemma: " << *best_lemma);
|
||||||
|
|
||||||
m_conflict.reset();
|
m_conflict.reset();
|
||||||
backjump(jump_level);
|
backjump(jump_level);
|
||||||
|
|
||||||
|
@ -1054,7 +1063,11 @@ namespace polysat {
|
||||||
|
|
||||||
void solver::assign_eval(sat::literal lit) {
|
void solver::assign_eval(sat::literal lit) {
|
||||||
signed_constraint const c = lit2cnstr(lit);
|
signed_constraint const c = lit2cnstr(lit);
|
||||||
|
LOG_V(10, "Evaluate: " << lit_pp(*this ,lit));
|
||||||
|
// assertion is false
|
||||||
|
if (!c.is_currently_true(*this)) IF_VERBOSE(0, verbose_stream() << c << " is not currently true\n");
|
||||||
SASSERT(c.is_currently_true(*this));
|
SASSERT(c.is_currently_true(*this));
|
||||||
|
VERIFY(c.is_currently_true(*this));
|
||||||
unsigned level = 0;
|
unsigned level = 0;
|
||||||
// NOTE: constraint may be evaluated even if some variables are still unassigned (e.g., 0*x doesn't depend on x).
|
// NOTE: constraint may be evaluated even if some variables are still unassigned (e.g., 0*x doesn't depend on x).
|
||||||
for (auto v : c->vars())
|
for (auto v : c->vars())
|
||||||
|
@ -1309,12 +1322,10 @@ namespace polysat {
|
||||||
for (auto c : m_constraints)
|
for (auto c : m_constraints)
|
||||||
out << "\t" << c->bvar2string() << ": " << *c << "\n";
|
out << "\t" << c->bvar2string() << ": " << *c << "\n";
|
||||||
out << "Clauses:\n";
|
out << "Clauses:\n";
|
||||||
for (auto const& cls : m_constraints.clauses()) {
|
for (clause const& cl : m_constraints.clauses()) {
|
||||||
for (auto const& cl : cls) {
|
out << "\t" << cl << "\n";
|
||||||
out << "\t" << *cl << "\n";
|
for (sat::literal lit : cl)
|
||||||
for (auto lit : *cl)
|
out << "\t\t" << lit << ": " << lit2cnstr(lit) << "\n";
|
||||||
out << "\t\t" << lit << ": " << lit2cnstr(lit) << "\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -1430,23 +1441,20 @@ namespace polysat {
|
||||||
// Check for missed boolean propagations:
|
// Check for missed boolean propagations:
|
||||||
// - no clause should have exactly one unassigned literal, unless it is already true.
|
// - no clause should have exactly one unassigned literal, unless it is already true.
|
||||||
// - no clause should be false
|
// - no clause should be false
|
||||||
for (auto const& cls : m_constraints.clauses()) {
|
for (clause const& cl : m_constraints.clauses()) {
|
||||||
for (auto const& clref : cls) {
|
bool const is_true = any_of(cl, [&](auto lit) { return m_bvars.is_true(lit); });
|
||||||
clause const& cl = *clref;
|
if (is_true)
|
||||||
bool const is_true = any_of(cl, [&](auto lit) { return m_bvars.is_true(lit); });
|
continue;
|
||||||
if (is_true)
|
size_t const undefs = count_if(cl, [&](auto lit) { return !m_bvars.is_assigned(lit); });
|
||||||
continue;
|
if (undefs == 1) {
|
||||||
size_t const undefs = count_if(cl, [&](auto lit) { return !m_bvars.is_assigned(lit); });
|
LOG("Missed boolean propagation of clause: " << cl);
|
||||||
if (undefs == 1) {
|
for (sat::literal lit : cl) {
|
||||||
LOG("Missed boolean propagation of clause: " << cl);
|
LOG(" " << lit_pp(*this, lit));
|
||||||
for (sat::literal lit : cl) {
|
|
||||||
LOG(" " << lit_pp(*this, lit));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SASSERT(undefs != 1);
|
|
||||||
bool const is_false = all_of(cl, [&](auto lit) { return m_bvars.is_false(lit); });
|
|
||||||
SASSERT(!is_false);
|
|
||||||
}
|
}
|
||||||
|
SASSERT(undefs != 1);
|
||||||
|
bool const is_false = all_of(cl, [&](auto lit) { return m_bvars.is_false(lit); });
|
||||||
|
SASSERT(!is_false);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1456,7 +1464,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check that boolean assignment and constraint evaluation are consistent */
|
/** Check that boolean assignment and constraint evaluation are consistent */
|
||||||
bool solver::assignment_invariant() {
|
bool solver::assignment_invariant() const {
|
||||||
if (is_conflict())
|
if (is_conflict())
|
||||||
return true;
|
return true;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
@ -1475,6 +1483,25 @@ namespace polysat {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check that each variable is either assigned or queued for decisions */
|
||||||
|
bool solver::var_queue_invariant() const {
|
||||||
|
if (is_conflict())
|
||||||
|
return true;
|
||||||
|
uint_set active;
|
||||||
|
for (pvar v : m_free_pvars)
|
||||||
|
active.insert(v);
|
||||||
|
for (auto const& [v, val] : assignment())
|
||||||
|
active.insert(v);
|
||||||
|
bool ok = true;
|
||||||
|
for (pvar v = 0; v < num_vars(); ++v) {
|
||||||
|
if (!active.contains(v)) {
|
||||||
|
ok = false;
|
||||||
|
LOG("Lost variable v" << v << " (it is neither assigned nor free)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
/// Check that all constraints on the stack are satisfied by the current model.
|
/// Check that all constraints on the stack are satisfied by the current model.
|
||||||
bool solver::verify_sat() {
|
bool solver::verify_sat() {
|
||||||
LOG_H1("Checking current model...");
|
LOG_H1("Checking current model...");
|
||||||
|
@ -1487,19 +1514,17 @@ namespace polysat {
|
||||||
all_ok = all_ok && ok;
|
all_ok = all_ok && ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto clauses : m_constraints.clauses()) {
|
for (clause const& cl : m_constraints.clauses()) {
|
||||||
for (auto cl : clauses) {
|
bool clause_ok = false;
|
||||||
bool clause_ok = false;
|
for (sat::literal lit : cl) {
|
||||||
for (sat::literal lit : *cl) {
|
bool ok = lit2cnstr(lit).is_currently_true(*this);
|
||||||
bool ok = lit2cnstr(lit).is_currently_true(*this);
|
if (ok) {
|
||||||
if (ok) {
|
clause_ok = true;
|
||||||
clause_ok = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LOG((clause_ok ? "PASS" : "FAIL") << ": " << show_deref(cl) << (cl->is_redundant() ? " (redundant)" : ""));
|
|
||||||
all_ok = all_ok && clause_ok;
|
|
||||||
}
|
}
|
||||||
|
LOG((clause_ok ? "PASS" : "FAIL") << ": " << cl << (cl.is_redundant() ? " (redundant)" : ""));
|
||||||
|
all_ok = all_ok && clause_ok;
|
||||||
}
|
}
|
||||||
if (all_ok) LOG("All good!");
|
if (all_ok) LOG("All good!");
|
||||||
return all_ok;
|
return all_ok;
|
||||||
|
|
|
@ -157,6 +157,7 @@ namespace polysat {
|
||||||
bool_var_manager m_bvars; // Map boolean variables to constraints
|
bool_var_manager m_bvars; // Map boolean variables to constraints
|
||||||
var_queue m_free_pvars; // free poly vars
|
var_queue m_free_pvars; // free poly vars
|
||||||
stats m_stats;
|
stats m_stats;
|
||||||
|
random_gen m_rand;
|
||||||
|
|
||||||
config m_config;
|
config m_config;
|
||||||
// Per constraint state
|
// Per constraint state
|
||||||
|
@ -188,7 +189,7 @@ namespace polysat {
|
||||||
constraints m_pwatch_trail;
|
constraints m_pwatch_trail;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ptr_vector<clause> m_lemmas; ///< the non-asserting lemmas
|
ptr_vector<clause const> m_lemmas; ///< the non-asserting lemmas
|
||||||
unsigned m_lemmas_qhead = 0;
|
unsigned m_lemmas_qhead = 0;
|
||||||
|
|
||||||
unsigned_vector m_base_levels; // External clients can push/pop scope.
|
unsigned_vector m_base_levels; // External clients can push/pop scope.
|
||||||
|
@ -215,6 +216,8 @@ namespace polysat {
|
||||||
dd::pdd_manager& sz2pdd(unsigned sz) const;
|
dd::pdd_manager& sz2pdd(unsigned sz) const;
|
||||||
dd::pdd_manager& var2pdd(pvar v) const;
|
dd::pdd_manager& var2pdd(pvar v) const;
|
||||||
|
|
||||||
|
pvar num_vars() const { return m_value.size(); }
|
||||||
|
|
||||||
assignment_t const& assignment() const { return m_search.assignment(); }
|
assignment_t const& assignment() const { return m_search.assignment(); }
|
||||||
|
|
||||||
void push_level();
|
void push_level();
|
||||||
|
@ -251,7 +254,8 @@ namespace polysat {
|
||||||
void set_conflict_at_base_level() { m_conflict.init_at_base_level(); }
|
void set_conflict_at_base_level() { m_conflict.init_at_base_level(); }
|
||||||
void set_conflict(signed_constraint c) { m_conflict.init(c); }
|
void set_conflict(signed_constraint c) { m_conflict.init(c); }
|
||||||
void set_conflict(clause& cl) { m_conflict.init(cl); }
|
void set_conflict(clause& cl) { m_conflict.init(cl); }
|
||||||
void set_conflict(pvar v, bool by_viable_fallback) { m_conflict.init(v, by_viable_fallback); }
|
void set_conflict_by_viable_interval(pvar v) { m_conflict.init_by_viable_interval(v); }
|
||||||
|
void set_conflict_by_viable_fallback(pvar v, univariate_solver& us) { m_conflict.init_by_viable_fallback(v, us); }
|
||||||
|
|
||||||
bool can_decide() const;
|
bool can_decide() const;
|
||||||
bool can_bdecide() const;
|
bool can_bdecide() const;
|
||||||
|
@ -309,7 +313,8 @@ namespace polysat {
|
||||||
static bool invariant(signed_constraints const& cs);
|
static bool invariant(signed_constraints const& cs);
|
||||||
bool wlist_invariant() const;
|
bool wlist_invariant() const;
|
||||||
bool bool_watch_invariant() const;
|
bool bool_watch_invariant() const;
|
||||||
bool assignment_invariant();
|
bool assignment_invariant() const;
|
||||||
|
bool var_queue_invariant() const;
|
||||||
bool verify_sat();
|
bool verify_sat();
|
||||||
|
|
||||||
bool can_propagate();
|
bool can_propagate();
|
||||||
|
@ -417,15 +422,33 @@ namespace polysat {
|
||||||
signed_constraint eq(pdd const& p, unsigned q) { return eq(p - q); }
|
signed_constraint eq(pdd const& p, unsigned q) { return eq(p - q); }
|
||||||
signed_constraint odd(pdd const& p) { return ~even(p); }
|
signed_constraint odd(pdd const& p) { return ~even(p); }
|
||||||
signed_constraint even(pdd const& p) { return parity(p, 1); }
|
signed_constraint even(pdd const& p) { return parity(p, 1); }
|
||||||
/** parity(p) >= k (<=> p * 2^(K-k) == 0) */
|
/** parity(p) >= k */
|
||||||
signed_constraint parity(pdd const& p, unsigned k) {
|
signed_constraint parity(pdd const& p, unsigned k) { // TODO: rename to parity_at_least?
|
||||||
unsigned N = p.manager().power_of_2();
|
unsigned N = p.manager().power_of_2();
|
||||||
|
// parity(p) >= k
|
||||||
|
// <=> p * 2^(N - k) == 0
|
||||||
if (k >= N)
|
if (k >= N)
|
||||||
return eq(p);
|
return eq(p);
|
||||||
else if (k == 0)
|
else if (k == 0) {
|
||||||
return odd(p);
|
// parity(p) >= 0 is a tautology
|
||||||
|
verbose_stream() << "REDUNDANT parity constraint: parity(" << p << ", " << k << ")\n";
|
||||||
|
return eq(p.manager().zero());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return eq(p*rational::power_of_two(N - k));
|
return eq(p * rational::power_of_two(N - k));
|
||||||
|
}
|
||||||
|
/** parity(p) <= k */
|
||||||
|
signed_constraint parity_at_most(pdd const& p, unsigned k) {
|
||||||
|
unsigned N = p.manager().power_of_2();
|
||||||
|
// parity(p) <= k
|
||||||
|
// <=> ~(parity(p) >= k+1)
|
||||||
|
if (k >= N) {
|
||||||
|
// parity(p) <= N is a tautology
|
||||||
|
verbose_stream() << "REDUNDANT parity constraint: parity(" << p << ", " << k << ")\n";
|
||||||
|
return eq(p.manager().zero());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return ~parity(p, k + 1);
|
||||||
}
|
}
|
||||||
signed_constraint diseq(pdd const& p, rational const& q) { return diseq(p - q); }
|
signed_constraint diseq(pdd const& p, rational const& q) { return diseq(p - q); }
|
||||||
signed_constraint diseq(pdd const& p, unsigned q) { return diseq(p - q); }
|
signed_constraint diseq(pdd const& p, unsigned q) { return diseq(p - q); }
|
||||||
|
|
|
@ -135,8 +135,8 @@ namespace {
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
ule_constraint::ule_constraint(constraint_manager& m, pdd const& l, pdd const& r) :
|
ule_constraint::ule_constraint(pdd const& l, pdd const& r) :
|
||||||
constraint(m, ckind_t::ule_t), m_lhs(l), m_rhs(r) {
|
constraint(ckind_t::ule_t), m_lhs(l), m_rhs(r) {
|
||||||
|
|
||||||
m_vars.append(m_lhs.free_vars());
|
m_vars.append(m_lhs.free_vars());
|
||||||
for (auto v : m_rhs.free_vars())
|
for (auto v : m_rhs.free_vars())
|
||||||
|
@ -185,9 +185,9 @@ namespace polysat {
|
||||||
signed_constraint sc(this, is_positive);
|
signed_constraint sc(this, is_positive);
|
||||||
|
|
||||||
LOG_H3("Narrowing " << sc);
|
LOG_H3("Narrowing " << sc);
|
||||||
LOG_V("Assignment: " << assignments_pp(s));
|
LOG_V(10, "Assignment: " << assignments_pp(s));
|
||||||
LOG_V("Substituted LHS: " << lhs() << " := " << p);
|
LOG_V(10, "Substituted LHS: " << lhs() << " := " << p);
|
||||||
LOG_V("Substituted RHS: " << rhs() << " := " << q);
|
LOG_V(10, "Substituted RHS: " << rhs() << " := " << q);
|
||||||
|
|
||||||
if (is_always_false(is_positive, p, q)) {
|
if (is_always_false(is_positive, p, q)) {
|
||||||
s.set_conflict(sc);
|
s.set_conflict(sc);
|
||||||
|
@ -353,9 +353,16 @@ namespace polysat {
|
||||||
return other.is_ule() && lhs() == other.to_ule().lhs() && rhs() == other.to_ule().rhs();
|
return other.is_ule() && lhs() == other.to_ule().lhs() && rhs() == other.to_ule().rhs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ule_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
void ule_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
||||||
auto p_coeff = s.subst(lhs()).get_univariate_coefficients();
|
pdd p = s.subst(lhs());
|
||||||
auto q_coeff = s.subst(rhs()).get_univariate_coefficients();
|
pdd q = s.subst(rhs());
|
||||||
us.add_ule(p_coeff, q_coeff, !is_positive, dep);
|
bool p_ok = p.is_univariate_in(v);
|
||||||
|
bool q_ok = q.is_univariate_in(v);
|
||||||
|
if (!is_positive && !q_ok) // add p > 0
|
||||||
|
us.add_ugt(p.get_univariate_coefficients(), rational::zero(), false, dep);
|
||||||
|
if (!is_positive && !p_ok) // add -1 > q <==> q+1 > 0
|
||||||
|
us.add_ugt((q + 1).get_univariate_coefficients(), rational::zero(), false, dep);
|
||||||
|
if (p_ok && q_ok)
|
||||||
|
us.add_ule(p.get_univariate_coefficients(), q.get_univariate_coefficients(), !is_positive, dep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace polysat {
|
||||||
pdd m_lhs;
|
pdd m_lhs;
|
||||||
pdd m_rhs;
|
pdd m_rhs;
|
||||||
|
|
||||||
ule_constraint(constraint_manager& m, pdd const& l, pdd const& r);
|
ule_constraint(pdd const& l, pdd const& r);
|
||||||
static void simplify(bool& is_positive, pdd& lhs, pdd& rhs);
|
static void simplify(bool& is_positive, pdd& lhs, pdd& rhs);
|
||||||
static bool is_always_true(bool is_positive, pdd const& lhs, pdd const& rhs) { return eval(lhs, rhs) == to_lbool(is_positive); }
|
static bool is_always_true(bool is_positive, pdd const& lhs, pdd const& rhs) { return eval(lhs, rhs) == to_lbool(is_positive); }
|
||||||
static bool is_always_false(bool is_positive, pdd const& lhs, pdd const& rhs) { return is_always_true(!is_positive, lhs, rhs); }
|
static bool is_always_false(bool is_positive, pdd const& lhs, pdd const& rhs) { return is_always_true(!is_positive, lhs, rhs); }
|
||||||
|
@ -45,7 +45,7 @@ namespace polysat {
|
||||||
unsigned hash() const override;
|
unsigned hash() const override;
|
||||||
bool operator==(constraint const& other) const override;
|
bool operator==(constraint const& other) const override;
|
||||||
bool is_eq() const override { return m_rhs.is_zero(); }
|
bool is_eq() const override { return m_rhs.is_zero(); }
|
||||||
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ule_pp {
|
struct ule_pp {
|
||||||
|
|
|
@ -15,8 +15,8 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
umul_ovfl_constraint::umul_ovfl_constraint(constraint_manager& m, pdd const& p, pdd const& q):
|
umul_ovfl_constraint::umul_ovfl_constraint(pdd const& p, pdd const& q):
|
||||||
constraint(m, ckind_t::umul_ovfl_t), m_p(p), m_q(q) {
|
constraint(ckind_t::umul_ovfl_t), m_p(p), m_q(q) {
|
||||||
simplify();
|
simplify();
|
||||||
m_vars.append(m_p.free_vars());
|
m_vars.append(m_p.free_vars());
|
||||||
for (auto v : m_q.free_vars())
|
for (auto v : m_q.free_vars())
|
||||||
|
@ -25,8 +25,7 @@ namespace polysat {
|
||||||
|
|
||||||
}
|
}
|
||||||
void umul_ovfl_constraint::simplify() {
|
void umul_ovfl_constraint::simplify() {
|
||||||
if (m_p.is_zero() || m_q.is_zero() ||
|
if (m_p.is_zero() || m_q.is_zero() || m_p.is_one() || m_q.is_one()) {
|
||||||
m_p.is_one() || m_q.is_one()) {
|
|
||||||
m_q = 0;
|
m_q = 0;
|
||||||
m_p = 0;
|
m_p = 0;
|
||||||
return;
|
return;
|
||||||
|
@ -99,8 +98,8 @@ namespace polysat {
|
||||||
signed_constraint sc(this, is_positive);
|
signed_constraint sc(this, is_positive);
|
||||||
// ¬Omega(p, q) ==> q = 0 \/ p <= p*q
|
// ¬Omega(p, q) ==> q = 0 \/ p <= p*q
|
||||||
// ¬Omega(p, q) ==> p = 0 \/ q <= p*q
|
// ¬Omega(p, q) ==> p = 0 \/ q <= p*q
|
||||||
s.add_clause(~sc, /* s.eq(p()), */ s.eq(q()), s.ule(p(), p()*q()), false);
|
s.add_clause(~sc, s.eq(q()), s.ule(p(), p()*q()), false);
|
||||||
s.add_clause(~sc, s.eq(p()), /* s.eq(q()), */ s.ule(q(), p()*q()), false);
|
s.add_clause(~sc, s.eq(p()), s.ule(q(), p()*q()), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,9 +157,13 @@ namespace polysat {
|
||||||
return other.is_umul_ovfl() && p() == other.to_umul_ovfl().p() && q() == other.to_umul_ovfl().q();
|
return other.is_umul_ovfl() && p() == other.to_umul_ovfl().p() && q() == other.to_umul_ovfl().q();
|
||||||
}
|
}
|
||||||
|
|
||||||
void umul_ovfl_constraint::add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
void umul_ovfl_constraint::add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const {
|
||||||
auto p_coeff = s.subst(p()).get_univariate_coefficients();
|
pdd p1 = s.subst(p());
|
||||||
auto q_coeff = s.subst(q()).get_univariate_coefficients();
|
if (!p1.is_univariate_in(v))
|
||||||
us.add_umul_ovfl(p_coeff, q_coeff, !is_positive, dep);
|
return;
|
||||||
|
pdd q1 = s.subst(q());
|
||||||
|
if (!q1.is_univariate_in(v))
|
||||||
|
return;
|
||||||
|
us.add_umul_ovfl(p1.get_univariate_coefficients(), q1.get_univariate_coefficients(), !is_positive, dep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace polysat {
|
||||||
pdd m_p;
|
pdd m_p;
|
||||||
pdd m_q;
|
pdd m_q;
|
||||||
|
|
||||||
umul_ovfl_constraint(constraint_manager& m, pdd const& p, pdd const& q);
|
umul_ovfl_constraint(pdd const& p, pdd const& q);
|
||||||
void simplify();
|
void simplify();
|
||||||
static bool is_always_true(bool is_positive, pdd const& p, pdd const& q) { return eval(p, q) == to_lbool(is_positive); }
|
static bool is_always_true(bool is_positive, pdd const& p, pdd const& q) { return eval(p, q) == to_lbool(is_positive); }
|
||||||
static bool is_always_false(bool is_positive, pdd const& p, pdd const& q) { return is_always_true(!is_positive, p, q); }
|
static bool is_always_false(bool is_positive, pdd const& p, pdd const& q) { return is_always_true(!is_positive, p, q); }
|
||||||
|
@ -46,7 +46,7 @@ namespace polysat {
|
||||||
bool operator==(constraint const& other) const override;
|
bool operator==(constraint const& other) const override;
|
||||||
bool is_eq() const override { return false; }
|
bool is_eq() const override { return false; }
|
||||||
|
|
||||||
void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
void add_to_univariate_solver(pvar v, solver& s, univariate_solver& us, unsigned dep, bool is_positive) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,12 @@ Author:
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
univariate_solver::dep_vector univariate_solver::unsat_core() {
|
||||||
|
dep_vector deps;
|
||||||
|
unsat_core(deps);
|
||||||
|
return deps;
|
||||||
|
}
|
||||||
|
|
||||||
class univariate_bitblast_solver : public univariate_solver {
|
class univariate_bitblast_solver : public univariate_solver {
|
||||||
// TODO: does it make sense to share m and bv between different solver instances?
|
// TODO: does it make sense to share m and bv between different solver instances?
|
||||||
// TODO: consider pooling solvers to save setup overhead, see if solver/solver_pool.h can be used
|
// TODO: consider pooling solvers to save setup overhead, see if solver/solver_pool.h can be used
|
||||||
|
@ -33,6 +39,7 @@ namespace polysat {
|
||||||
scoped_ptr<bv_util> bv;
|
scoped_ptr<bv_util> bv;
|
||||||
scoped_ptr<solver> s;
|
scoped_ptr<solver> s;
|
||||||
unsigned bit_width;
|
unsigned bit_width;
|
||||||
|
unsigned m_scope_level = 0;
|
||||||
func_decl_ref x_decl;
|
func_decl_ref x_decl;
|
||||||
expr_ref x;
|
expr_ref x;
|
||||||
vector<rational> model_cache;
|
vector<rational> model_cache;
|
||||||
|
@ -46,6 +53,7 @@ namespace polysat {
|
||||||
bv = alloc(bv_util, m);
|
bv = alloc(bv_util, m);
|
||||||
params_ref p;
|
params_ref p;
|
||||||
p.set_bool("bv.polysat", false);
|
p.set_bool("bv.polysat", false);
|
||||||
|
// p.set_bool("smt", true);
|
||||||
s = mk_solver(m, p, false, true, true, symbol::null);
|
s = mk_solver(m, p, false, true, true, symbol::null);
|
||||||
x_decl = m.mk_const_decl("x", bv->mk_sort(bit_width));
|
x_decl = m.mk_const_decl("x", bv->mk_sort(bit_width));
|
||||||
x = m.mk_const(x_decl);
|
x = m.mk_const(x_decl);
|
||||||
|
@ -59,7 +67,8 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_cache() {
|
void push_cache() {
|
||||||
model_cache.push_back(model_cache.back());
|
rational v = model_cache.back();
|
||||||
|
model_cache.push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop_cache() {
|
void pop_cache() {
|
||||||
|
@ -67,19 +76,37 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void push() override {
|
void push() override {
|
||||||
|
m_scope_level++;
|
||||||
push_cache();
|
push_cache();
|
||||||
s->push();
|
s->push();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(unsigned n) override {
|
void pop(unsigned n) override {
|
||||||
|
SASSERT(scope_level() >= n);
|
||||||
|
m_scope_level -= n;
|
||||||
pop_cache();
|
pop_cache();
|
||||||
s->pop(n);
|
s->pop(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned scope_level() override {
|
||||||
|
return m_scope_level;
|
||||||
|
}
|
||||||
|
|
||||||
expr* mk_numeral(rational const& r) const {
|
expr* mk_numeral(rational const& r) const {
|
||||||
return bv->mk_numeral(r, bit_width);
|
return bv->mk_numeral(r, bit_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_zero(univariate const& p) const {
|
||||||
|
for (auto n : p)
|
||||||
|
if (n != 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_zero(rational const& p) const {
|
||||||
|
return p.is_zero();
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// [d,c,b,a] --> ((a*x + b)*x + c)*x + d
|
// [d,c,b,a] --> ((a*x + b)*x + c)*x + d
|
||||||
expr* mk_poly(univariate const& p) const {
|
expr* mk_poly(univariate const& p) const {
|
||||||
|
@ -97,35 +124,43 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// TODO: shouldn't the simplification step of the underlying solver already support this transformation? how to enable?
|
|
||||||
// 2^k*x --> x << k
|
// 2^k*x --> x << k
|
||||||
// n*x --> n * x
|
// n*x --> n * x
|
||||||
expr* mk_poly_term(rational const& coeff, expr* xpow) const {
|
expr* mk_poly_term(rational const& coeff, expr* xpow) const {
|
||||||
unsigned pow;
|
unsigned pow;
|
||||||
|
SASSERT(!coeff.is_zero());
|
||||||
|
if (coeff.is_one())
|
||||||
|
return xpow;
|
||||||
if (coeff.is_power_of_two(pow))
|
if (coeff.is_power_of_two(pow))
|
||||||
return bv->mk_bv_shl(xpow, mk_numeral(rational(pow)));
|
return bv->mk_bv_shl(xpow, mk_numeral(rational(pow)));
|
||||||
else
|
return bv->mk_bv_mul(mk_numeral(coeff), xpow);
|
||||||
return bv->mk_bv_mul(mk_numeral(coeff), xpow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// [d,c,b,a] --> d + c*x + b*(x*x) + a*(x*x*x)
|
// [d,c,b,a] --> d + c*x + b*(x*x) + a*(x*x*x)
|
||||||
expr* mk_poly(univariate const& p) const {
|
expr_ref mk_poly(univariate const& p) {
|
||||||
if (p.empty()) {
|
expr_ref e(m);
|
||||||
return mk_numeral(rational::zero());
|
if (p.empty())
|
||||||
}
|
e = mk_numeral(rational::zero());
|
||||||
else {
|
else {
|
||||||
expr* e = mk_numeral(p[0]);
|
if (!p[0].is_zero())
|
||||||
expr* xpow = x;
|
e = mk_numeral(p[0]);
|
||||||
|
expr_ref xpow = x;
|
||||||
for (unsigned i = 1; i < p.size(); ++i) {
|
for (unsigned i = 1; i < p.size(); ++i) {
|
||||||
if (!p[i].is_zero()) {
|
if (!p[i].is_zero()) {
|
||||||
expr* t = mk_poly_term(p[i], xpow);
|
expr* t = mk_poly_term(p[i], xpow);
|
||||||
e = bv->mk_bv_add(e, t);
|
e = e ? bv->mk_bv_add(e, t) : t;
|
||||||
}
|
}
|
||||||
if (i + 1 < p.size())
|
if (i + 1 < p.size())
|
||||||
xpow = bv->mk_bv_mul(xpow, x);
|
xpow = bv->mk_bv_mul(xpow, x);
|
||||||
}
|
}
|
||||||
return e;
|
if (!e)
|
||||||
|
e = mk_numeral(p[0]);
|
||||||
}
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref mk_poly(rational const& p) {
|
||||||
|
return {mk_numeral(p), m};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -133,15 +168,29 @@ namespace polysat {
|
||||||
reset_cache();
|
reset_cache();
|
||||||
if (sign)
|
if (sign)
|
||||||
e = m.mk_not(e);
|
e = m.mk_not(e);
|
||||||
expr* a = m.mk_const(m.mk_const_decl(symbol(dep), m.mk_bool_sort()));
|
if (dep == null_dep) {
|
||||||
s->assert_expr(e, a);
|
s->assert_expr(e);
|
||||||
IF_VERBOSE(10, verbose_stream() << "(assert (! " << expr_ref(e, m) << " :named " << expr_ref(a, m) << "))\n");
|
IF_VERBOSE(10, verbose_stream() << "(assert " << expr_ref(e, m) << ")\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expr* a = m.mk_const(m.mk_const_decl(symbol(dep), m.mk_bool_sort()));
|
||||||
|
s->assert_expr(e, a);
|
||||||
|
IF_VERBOSE(10, verbose_stream() << "(assert (! " << expr_ref(e, m) << " :named " << expr_ref(a, m) << "))\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
|
template <typename lhs_t, typename rhs_t>
|
||||||
add(bv->mk_ule(mk_poly(lhs), mk_poly(rhs)), sign, dep);
|
void add_ule_impl(lhs_t const& lhs, rhs_t const& rhs, bool sign, dep_t dep) {
|
||||||
|
if (is_zero(rhs))
|
||||||
|
add(m.mk_eq(mk_poly(lhs), mk_poly(rhs)), sign, dep);
|
||||||
|
else
|
||||||
|
add(bv->mk_ule(mk_poly(lhs), mk_poly(rhs)), sign, dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
|
||||||
|
void add_ule(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
|
||||||
|
void add_ule(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) override { add_ule_impl(lhs, rhs, sign, dep); }
|
||||||
|
|
||||||
void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
|
void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) override {
|
||||||
add(bv->mk_bvumul_no_ovfl(mk_poly(lhs), mk_poly(rhs)), !sign, dep);
|
add(bv->mk_bvumul_no_ovfl(mk_poly(lhs), mk_poly(rhs)), !sign, dep);
|
||||||
}
|
}
|
||||||
|
@ -183,11 +232,14 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_ule_const(rational const& val, bool sign, dep_t dep) override {
|
void add_ule_const(rational const& val, bool sign, dep_t dep) override {
|
||||||
add(bv->mk_ule(x, mk_numeral(val)), sign, dep);
|
if (val == 0)
|
||||||
|
add(m.mk_eq(x, mk_poly(val)), sign, dep);
|
||||||
|
else
|
||||||
|
add(bv->mk_ule(x, mk_poly(val)), sign, dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_uge_const(rational const& val, bool sign, dep_t dep) override {
|
void add_uge_const(rational const& val, bool sign, dep_t dep) override {
|
||||||
add(bv->mk_ule(mk_numeral(val), x), sign, dep);
|
add(bv->mk_ule(mk_poly(val), x), sign, dep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_bit(unsigned idx, bool sign, dep_t dep) override {
|
void add_bit(unsigned idx, bool sign, dep_t dep) override {
|
||||||
|
@ -198,16 +250,16 @@ namespace polysat {
|
||||||
return s->check_sat();
|
return s->check_sat();
|
||||||
}
|
}
|
||||||
|
|
||||||
dep_vector unsat_core() override {
|
void unsat_core(dep_vector& deps) override {
|
||||||
dep_vector deps;
|
deps.reset();
|
||||||
expr_ref_vector core(m);
|
expr_ref_vector core(m);
|
||||||
s->get_unsat_core(core);
|
s->get_unsat_core(core);
|
||||||
for (expr* a : core) {
|
for (expr* a : core) {
|
||||||
unsigned dep = to_app(a)->get_decl()->get_name().get_num();
|
unsigned dep = to_app(a)->get_decl()->get_name().get_num();
|
||||||
deps.push_back(dep);
|
deps.push_back(dep);
|
||||||
}
|
}
|
||||||
|
IF_VERBOSE(10, verbose_stream() << "core " << deps << "\n");
|
||||||
SASSERT(deps.size() > 0);
|
SASSERT(deps.size() > 0);
|
||||||
return deps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rational model() override {
|
rational model() override {
|
||||||
|
@ -232,7 +284,7 @@ namespace polysat {
|
||||||
// try reducing val by setting bits to 0, starting at the msb.
|
// try reducing val by setting bits to 0, starting at the msb.
|
||||||
for (unsigned k = bit_width; k-- > 0; ) {
|
for (unsigned k = bit_width; k-- > 0; ) {
|
||||||
if (!val.get_bit(k)) {
|
if (!val.get_bit(k)) {
|
||||||
add_bit0(k, 0);
|
add_bit0(k, null_dep);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// try decreasing k-th bit
|
// try decreasing k-th bit
|
||||||
|
@ -245,9 +297,9 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
pop(1);
|
pop(1);
|
||||||
if (result == l_true)
|
if (result == l_true)
|
||||||
add_bit0(k, 0);
|
add_bit0(k, null_dep);
|
||||||
else if (result == l_false)
|
else if (result == l_false)
|
||||||
add_bit1(k, 0);
|
add_bit1(k, null_dep);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -274,9 +326,9 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
pop(1);
|
pop(1);
|
||||||
if (result == l_true)
|
if (result == l_true)
|
||||||
add_bit1(k, 0);
|
add_bit1(k, null_dep);
|
||||||
else if (result == l_false)
|
else if (result == l_false)
|
||||||
add_bit0(k, 0);
|
add_bit0(k, null_dep);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -284,6 +336,27 @@ namespace polysat {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool find_two(rational& out1, rational& out2) override {
|
||||||
|
out1 = model();
|
||||||
|
bool ok = true;
|
||||||
|
push();
|
||||||
|
add(m.mk_eq(mk_numeral(out1), x), true, null_dep);
|
||||||
|
switch (check()) {
|
||||||
|
case l_true:
|
||||||
|
out2 = model();
|
||||||
|
break;
|
||||||
|
case l_false:
|
||||||
|
out2 = out1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pop(1);
|
||||||
|
IF_VERBOSE(10, verbose_stream() << "viable " << out1 << " " << out2 << "\n");
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& display(std::ostream& out) const override {
|
std::ostream& display(std::ostream& out) const override {
|
||||||
return out << *s;
|
return out << *s;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,17 +35,21 @@ namespace polysat {
|
||||||
/// e.g., the vector [ c, b, a ] represents a*x^2 + b*x + c.
|
/// e.g., the vector [ c, b, a ] represents a*x^2 + b*x + c.
|
||||||
using univariate = vector<rational>;
|
using univariate = vector<rational>;
|
||||||
|
|
||||||
|
const dep_t null_dep = UINT_MAX;
|
||||||
|
|
||||||
virtual ~univariate_solver() = default;
|
virtual ~univariate_solver() = default;
|
||||||
|
|
||||||
virtual void push() = 0;
|
virtual void push() = 0;
|
||||||
virtual void pop(unsigned n) = 0;
|
virtual void pop(unsigned n) = 0;
|
||||||
|
virtual unsigned scope_level() = 0;
|
||||||
|
|
||||||
virtual lbool check() = 0;
|
virtual lbool check() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Precondition: check() returned l_false
|
* Precondition: check() returned l_false
|
||||||
*/
|
*/
|
||||||
virtual dep_vector unsat_core() = 0;
|
dep_vector unsat_core();
|
||||||
|
virtual void unsat_core(dep_vector& out_deps) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Precondition: check() returned l_true
|
* Precondition: check() returned l_true
|
||||||
|
@ -68,7 +72,31 @@ namespace polysat {
|
||||||
*/
|
*/
|
||||||
virtual bool find_max(rational& out_max) = 0;
|
virtual bool find_max(rational& out_max) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find up to two viable values.
|
||||||
|
*
|
||||||
|
* Precondition: check() returned l_true
|
||||||
|
* returns: true on success, false on resource out
|
||||||
|
*/
|
||||||
|
virtual bool find_two(rational& out1, rational& out2) = 0;
|
||||||
|
|
||||||
|
/** lhs <= rhs */
|
||||||
virtual void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
virtual void add_ule(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
||||||
|
virtual void add_ule(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) = 0;
|
||||||
|
virtual void add_ule(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
||||||
|
/** lhs >= rhs */
|
||||||
|
void add_uge(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, sign, dep); }
|
||||||
|
void add_uge(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, sign, dep); }
|
||||||
|
void add_uge(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, sign, dep); }
|
||||||
|
/** lhs < rhs */
|
||||||
|
void add_ult(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, !sign, dep); }
|
||||||
|
void add_ult(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, !sign, dep); }
|
||||||
|
void add_ult(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(rhs, lhs, !sign, dep); }
|
||||||
|
/** lhs > rhs */
|
||||||
|
void add_ugt(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(lhs, rhs, !sign, dep); }
|
||||||
|
void add_ugt(univariate const& lhs, rational const& rhs, bool sign, dep_t dep) { add_ule(lhs, rhs, !sign, dep); }
|
||||||
|
void add_ugt(rational const& lhs, univariate const& rhs, bool sign, dep_t dep) { add_ule(lhs, rhs, !sign, dep); }
|
||||||
|
|
||||||
virtual void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
virtual void add_umul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
||||||
virtual void add_smul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
virtual void add_smul_ovfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
||||||
virtual void add_smul_udfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
virtual void add_smul_udfl(univariate const& lhs, univariate const& rhs, bool sign, dep_t dep) = 0;
|
||||||
|
|
|
@ -23,16 +23,26 @@ TODO: improve management of the fallback univariate solvers:
|
||||||
- incrementally add/remove constraints
|
- incrementally add/remove constraints
|
||||||
- set resource limit of univariate solver
|
- set resource limit of univariate solver
|
||||||
|
|
||||||
|
TODO: plan to fix the FI "pumping":
|
||||||
|
1. simple looping detection and bitblasting fallback. -- done
|
||||||
|
2. intervals at multiple bit widths
|
||||||
|
- for equations, this will give us exact solutions for all coefficients
|
||||||
|
- for inequalities, a coefficient 2^k*a means that intervals are periodic because the upper k bits of x are irrelevant;
|
||||||
|
storing the interval for x[K-k:0] would take care of this.
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
|
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
#include "math/polysat/viable.h"
|
#include "math/polysat/viable.h"
|
||||||
#include "math/polysat/solver.h"
|
#include "math/polysat/solver.h"
|
||||||
|
#include "math/polysat/number.h"
|
||||||
#include "math/polysat/univariate/univariate_solver.h"
|
#include "math/polysat/univariate/univariate_solver.h"
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
using namespace viable_query;
|
||||||
|
|
||||||
struct inf_fi : public inference {
|
struct inf_fi : public inference {
|
||||||
viable& v;
|
viable& v;
|
||||||
pvar var;
|
pvar var;
|
||||||
|
@ -137,7 +147,7 @@ namespace polysat {
|
||||||
prop = true;
|
prop = true;
|
||||||
break;
|
break;
|
||||||
case find_t::empty:
|
case find_t::empty:
|
||||||
s.set_conflict(v, false);
|
SASSERT(s.is_conflict());
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -159,6 +169,21 @@ namespace polysat {
|
||||||
// - side conditions
|
// - side conditions
|
||||||
// - i.lo() == i.lo_val() for each unit interval i
|
// - i.lo() == i.lo_val() for each unit interval i
|
||||||
// - i.hi() == i.hi_val() for each unit interval i
|
// - i.hi() == i.hi_val() for each unit interval i
|
||||||
|
|
||||||
|
// NSB review:
|
||||||
|
// the bounds added by x < p and p < x in forbidden_intervals
|
||||||
|
// match_non_max, match_non_zero
|
||||||
|
// use values that are approximations. Then the propagations in
|
||||||
|
// try_assign_eval are incorrect.
|
||||||
|
// For example, x > p means x has forbidden interval [0, p + 1[,
|
||||||
|
// the numeric interval is [0, 1[, but p + 1 == 1 is not ensured
|
||||||
|
// even p may have free variables.
|
||||||
|
// the proper side condition on p + 1 is -1 > p or -2 >= p or p + 1 != 0
|
||||||
|
// I am disabling match_non_max and match_non_zero from forbidden_interval
|
||||||
|
// The narrowing rules in ule_constraint already handle the bounds propagaitons
|
||||||
|
// as it propagates p != -1 and 0 != q (p < -1, q > 0),
|
||||||
|
//
|
||||||
|
|
||||||
for (auto const& c : get_constraints(v)) {
|
for (auto const& c : get_constraints(v)) {
|
||||||
s.try_assign_eval(c);
|
s.try_assign_eval(c);
|
||||||
}
|
}
|
||||||
|
@ -299,8 +324,14 @@ namespace polysat {
|
||||||
if (!e)
|
if (!e)
|
||||||
return true;
|
return true;
|
||||||
entry const* first = e;
|
entry const* first = e;
|
||||||
rational const& max_value = s.var2pdd(v).max_value();
|
auto& m = s.var2pdd(v);
|
||||||
rational mod_value = max_value + 1;
|
unsigned const N = m.power_of_2();
|
||||||
|
rational const& max_value = m.max_value();
|
||||||
|
rational const& mod_value = m.two_to_N();
|
||||||
|
|
||||||
|
// Rotate the 'first' entry, to prevent getting stuck in a refinement loop
|
||||||
|
// with an early entry when a later entry could give a better interval.
|
||||||
|
m_equal_lin[v] = m_equal_lin[v]->next();
|
||||||
|
|
||||||
auto delta_l = [&](rational const& coeff_val) {
|
auto delta_l = [&](rational const& coeff_val) {
|
||||||
return floor((coeff_val - e->interval.lo_val()) / e->coeff);
|
return floor((coeff_val - e->interval.lo_val()) / e->coeff);
|
||||||
|
@ -338,22 +369,79 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
do {
|
do {
|
||||||
LOG("refine-equal-lin for src: " << e->src);
|
|
||||||
rational coeff_val = mod(e->coeff * val, mod_value);
|
rational coeff_val = mod(e->coeff * val, mod_value);
|
||||||
if (e->interval.currently_contains(coeff_val)) {
|
if (e->interval.currently_contains(coeff_val)) {
|
||||||
|
LOG("refine-equal-lin for src: " << lit_pp(s, e->src));
|
||||||
|
LOG("forbidden interval v" << v << " " << num_pp(s, v, val) << " " << num_pp(s, v, e->coeff, true) << " * " << e->interval);
|
||||||
|
|
||||||
if (e->interval.lo_val().is_one() && e->interval.hi_val().is_zero() && e->coeff.is_odd()) {
|
if (mod(e->interval.hi_val() + 1, mod_value) == e->interval.lo_val()) {
|
||||||
rational lo(1);
|
// We have an equation: a * v == b
|
||||||
rational hi(0);
|
rational const a = e->coeff;
|
||||||
LOG("refine-equal-lin: " << " [" << lo << ", " << hi << "[");
|
rational const b = e->interval.hi_val();
|
||||||
pdd lop = s.var2pdd(v).mk_val(lo);
|
LOG("refine-equal-lin: equation detected: " << dd::val_pp(m, a, true) << " * v" << v << " == " << dd::val_pp(m, b, false));
|
||||||
pdd hip = s.var2pdd(v).mk_val(hi);
|
unsigned const parity_a = get_parity(a, N);
|
||||||
|
unsigned const parity_b = get_parity(b, N);
|
||||||
|
// LOG("a " << a << " parity " << parity_a);
|
||||||
|
// LOG("b " << b << " parity " << parity_b);
|
||||||
|
if (parity_a > parity_b) {
|
||||||
|
// No solution
|
||||||
|
LOG("refined: no solution due to parity");
|
||||||
|
entry* ne = alloc_entry();
|
||||||
|
ne->refined = e;
|
||||||
|
ne->src = e->src;
|
||||||
|
ne->side_cond = e->side_cond;
|
||||||
|
ne->coeff = 1;
|
||||||
|
ne->interval = eval_interval::full();
|
||||||
|
intersect(v, ne);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (parity_a == 0) {
|
||||||
|
// "fast path" for odd a
|
||||||
|
rational a_inv;
|
||||||
|
VERIFY(a.mult_inverse(N, a_inv));
|
||||||
|
rational const hi = mod(a_inv * b, mod_value);
|
||||||
|
rational const lo = mod(hi + 1, mod_value);
|
||||||
|
LOG("refined to [" << num_pp(s, v, lo) << ", " << num_pp(s, v, hi) << "[");
|
||||||
|
SASSERT_EQ(mod(a * hi, mod_value), b); // hi is the solution
|
||||||
|
entry* ne = alloc_entry();
|
||||||
|
ne->refined = e;
|
||||||
|
ne->src = e->src;
|
||||||
|
ne->side_cond = e->side_cond;
|
||||||
|
ne->coeff = 1;
|
||||||
|
ne->interval = eval_interval::proper(m.mk_val(lo), lo, m.mk_val(hi), hi);
|
||||||
|
SASSERT(ne->interval.currently_contains(val));
|
||||||
|
intersect(v, ne);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 2^k * v == a_inv * b
|
||||||
|
// 2^k solutions because only the lower N-k bits of v are fixed.
|
||||||
|
//
|
||||||
|
// Smallest solution is v0 == a_inv * (b >> k)
|
||||||
|
// Solutions are of the form v_i = v0 + 2^(N-k) * i for i in { 0, 1, ..., 2^k - 1 }.
|
||||||
|
// Forbidden intervals: [v_i + 1; v_{i+1}[ == [ v_i + 1; v_i + 2^(N-k) [
|
||||||
|
// We need the interval that covers val:
|
||||||
|
// v_i + 1 <= val < v_i + 2^(N-k)
|
||||||
|
//
|
||||||
|
// TODO: create one interval for v[N-k:] instead of 2^k intervals for v.
|
||||||
|
unsigned const k = parity_a;
|
||||||
|
rational const a_inv = a.pseudo_inverse(N);
|
||||||
|
unsigned const N_minus_k = N - k;
|
||||||
|
rational const two_to_N_minus_k = rational::power_of_two(N_minus_k);
|
||||||
|
rational const v0 = mod(a_inv * machine_div2k(b, k), two_to_N_minus_k);
|
||||||
|
SASSERT(mod(val, two_to_N_minus_k) != v0); // val is not a solution
|
||||||
|
rational const vi = v0 + clear_lower_bits(mod(val - v0, mod_value), N_minus_k);
|
||||||
|
rational const lo = mod(vi + 1, mod_value);
|
||||||
|
rational const hi = mod(vi + two_to_N_minus_k, mod_value);
|
||||||
|
LOG("refined to [" << num_pp(s, v, lo) << ", " << num_pp(s, v, hi) << "[");
|
||||||
|
SASSERT_EQ(mod(a * (lo - 1), mod_value), b); // lo-1 is a solution
|
||||||
|
SASSERT_EQ(mod(a * hi, mod_value), b); // hi is a solution
|
||||||
entry* ne = alloc_entry();
|
entry* ne = alloc_entry();
|
||||||
ne->refined = e;
|
ne->refined = e;
|
||||||
ne->src = e->src;
|
ne->src = e->src;
|
||||||
ne->side_cond = e->side_cond;
|
ne->side_cond = e->side_cond;
|
||||||
ne->coeff = 1;
|
ne->coeff = 1;
|
||||||
ne->interval = eval_interval::proper(lop, lo, hip, hi);
|
ne->interval = eval_interval::proper(m.mk_val(lo), lo, m.mk_val(hi), hi);
|
||||||
|
SASSERT(ne->interval.currently_contains(val));
|
||||||
intersect(v, ne);
|
intersect(v, ne);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -378,13 +466,11 @@ namespace polysat {
|
||||||
lo = val - lambda_l;
|
lo = val - lambda_l;
|
||||||
increase_hi(hi);
|
increase_hi(hi);
|
||||||
}
|
}
|
||||||
LOG("forbidden interval v" << v << " " << num_pp(s, v, val) << " " << num_pp(s, v, e->coeff, true) << " * " << e->interval << " [" << num_pp(s, v, lo) << ", " << num_pp(s, v, hi) << "[");
|
LOG("refined to [" << num_pp(s, v, lo) << ", " << num_pp(s, v, hi) << "[");
|
||||||
SASSERT(hi <= mod_value);
|
SASSERT(hi <= mod_value);
|
||||||
bool full = (lo == 0 && hi == mod_value);
|
bool full = (lo == 0 && hi == mod_value);
|
||||||
if (hi == mod_value)
|
if (hi == mod_value)
|
||||||
hi = 0;
|
hi = 0;
|
||||||
pdd lop = s.var2pdd(v).mk_val(lo);
|
|
||||||
pdd hip = s.var2pdd(v).mk_val(hi);
|
|
||||||
entry* ne = alloc_entry();
|
entry* ne = alloc_entry();
|
||||||
ne->refined = e;
|
ne->refined = e;
|
||||||
ne->src = e->src;
|
ne->src = e->src;
|
||||||
|
@ -393,7 +479,7 @@ namespace polysat {
|
||||||
if (full)
|
if (full)
|
||||||
ne->interval = eval_interval::full();
|
ne->interval = eval_interval::full();
|
||||||
else
|
else
|
||||||
ne->interval = eval_interval::proper(lop, lo, hip, hi);
|
ne->interval = eval_interval::proper(m.mk_val(lo), lo, m.mk_val(hi), hi);
|
||||||
intersect(v, ne);
|
intersect(v, ne);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -412,6 +498,10 @@ namespace polysat {
|
||||||
rational const& max_value = s.var2pdd(v).max_value();
|
rational const& max_value = s.var2pdd(v).max_value();
|
||||||
rational const mod_value = max_value + 1;
|
rational const mod_value = max_value + 1;
|
||||||
|
|
||||||
|
// Rotate the 'first' entry, to prevent getting stuck in a refinement loop
|
||||||
|
// with an early entry when a later entry could give a better interval.
|
||||||
|
m_diseq_lin[v] = m_diseq_lin[v]->next();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
LOG("refine-disequal-lin for src: " << e->src);
|
LOG("refine-disequal-lin for src: " << e->src);
|
||||||
// We compute an interval if the concrete value 'val' violates the constraint:
|
// We compute an interval if the concrete value 'val' violates the constraint:
|
||||||
|
@ -543,88 +633,146 @@ namespace polysat {
|
||||||
return refine_viable(v, val);
|
return refine_viable(v, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find_t viable::find_viable(pvar v, rational& lo) {
|
||||||
rational viable::min_viable(pvar v) {
|
rational hi;
|
||||||
refined:
|
switch (find_viable(v, lo, hi)) {
|
||||||
rational lo(0);
|
case l_true:
|
||||||
auto* e = m_units[v];
|
return (lo == hi) ? find_t::singleton : find_t::multiple;
|
||||||
if (!e && !refine_viable(v, lo))
|
case l_false:
|
||||||
goto refined;
|
return find_t::empty;
|
||||||
if (!e)
|
default:
|
||||||
return lo;
|
return find_t::resource_out;
|
||||||
entry* first = e;
|
|
||||||
entry* last = first->prev();
|
|
||||||
if (last->interval.currently_contains(lo))
|
|
||||||
lo = last->interval.hi_val();
|
|
||||||
do {
|
|
||||||
if (!e->interval.currently_contains(lo))
|
|
||||||
break;
|
|
||||||
lo = e->interval.hi_val();
|
|
||||||
e = e->next();
|
|
||||||
}
|
}
|
||||||
while (e != first);
|
|
||||||
if (!refine_viable(v, lo))
|
|
||||||
goto refined;
|
|
||||||
SASSERT(is_viable(v, lo));
|
|
||||||
return lo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rational viable::max_viable(pvar v) {
|
lbool viable::find_viable(pvar v, rational& lo, rational& hi) {
|
||||||
refined:
|
std::pair<rational&, rational&> args{lo, hi};
|
||||||
rational hi = s.var2pdd(v).max_value();
|
return query<query_t::find_viable>(v, args);
|
||||||
auto* e = m_units[v];
|
|
||||||
if (!e && !refine_viable(v, hi))
|
|
||||||
goto refined;
|
|
||||||
if (!e)
|
|
||||||
return hi;
|
|
||||||
entry* last = e->prev();
|
|
||||||
e = last;
|
|
||||||
do {
|
|
||||||
if (!e->interval.currently_contains(hi))
|
|
||||||
break;
|
|
||||||
hi = e->interval.lo_val() - 1;
|
|
||||||
e = e->prev();
|
|
||||||
}
|
|
||||||
while (e != last);
|
|
||||||
if (!refine_viable(v, hi))
|
|
||||||
goto refined;
|
|
||||||
SASSERT(is_viable(v, hi));
|
|
||||||
return hi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// template <viable::query_t mode>
|
lbool viable::min_viable(pvar v, rational& lo) {
|
||||||
find_t viable::query(query_t mode, pvar v, rational& lo, rational& hi) {
|
return query<query_t::min_viable>(v, lo);
|
||||||
SASSERT(mode == query_t::find_viable); // other modes are TODO
|
}
|
||||||
|
|
||||||
auto const& max_value = s.var2pdd(v).max_value();
|
lbool viable::max_viable(pvar v, rational& hi) {
|
||||||
|
return query<query_t::max_viable>(v, hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool viable::has_upper_bound(pvar v, rational& out_hi, vector<signed_constraint>& out_c) {
|
||||||
|
entry const* first = m_units[v];
|
||||||
|
entry const* e = first;
|
||||||
|
bool found = false;
|
||||||
|
out_c.reset();
|
||||||
|
do {
|
||||||
|
found = false;
|
||||||
|
do {
|
||||||
|
if (!e->refined) {
|
||||||
|
auto const& lo = e->interval.lo();
|
||||||
|
auto const& hi = e->interval.hi();
|
||||||
|
if (lo.is_val() && hi.is_val()) {
|
||||||
|
if (out_c.empty() && lo.val() > hi.val()) {
|
||||||
|
out_c.push_back(e->src);
|
||||||
|
out_hi = lo.val() - 1;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
else if (!out_c.empty() && lo.val() <= out_hi && out_hi < hi.val()) {
|
||||||
|
out_c.push_back(e->src);
|
||||||
|
out_hi = lo.val() - 1;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e = e->next();
|
||||||
|
}
|
||||||
|
while (e != first);
|
||||||
|
}
|
||||||
|
while (found);
|
||||||
|
return !out_c.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool viable::has_lower_bound(pvar v, rational& out_lo, vector<signed_constraint>& out_c) {
|
||||||
|
entry const* first = m_units[v];
|
||||||
|
entry const* e = first;
|
||||||
|
bool found = false;
|
||||||
|
out_c.reset();
|
||||||
|
do {
|
||||||
|
found = false;
|
||||||
|
do {
|
||||||
|
if (!e->refined) {
|
||||||
|
auto const& lo = e->interval.lo();
|
||||||
|
auto const& hi = e->interval.hi();
|
||||||
|
if (lo.is_val() && hi.is_val()) {
|
||||||
|
if (out_c.empty() && hi.val() != 0 && (lo.val() == 0 || lo.val() > hi.val())) {
|
||||||
|
out_c.push_back(e->src);
|
||||||
|
out_lo = hi.val();
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
else if (!out_c.empty() && lo.val() <= out_lo && out_lo < hi.val()) {
|
||||||
|
out_c.push_back(e->src);
|
||||||
|
out_lo = hi.val();
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e = e->next();
|
||||||
|
}
|
||||||
|
while (e != first);
|
||||||
|
}
|
||||||
|
while (found);
|
||||||
|
return !out_c.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <query_t mode>
|
||||||
|
lbool viable::query(pvar v, typename query_result<mode>::result_t& result) {
|
||||||
// max number of interval refinements before falling back to the univariate solver
|
// max number of interval refinements before falling back to the univariate solver
|
||||||
unsigned const refinement_budget = 1000;
|
unsigned const refinement_budget = 1000;
|
||||||
unsigned refinements = refinement_budget;
|
unsigned refinements = refinement_budget;
|
||||||
|
|
||||||
refined:
|
while (refinements--) {
|
||||||
|
lbool res = l_undef;
|
||||||
|
|
||||||
if (!refinements) {
|
if constexpr (mode == query_t::find_viable)
|
||||||
LOG("Refinement budget exhausted! Fall back to univariate solver.");
|
res = query_find(v, result.first, result.second);
|
||||||
return find_t::resource_out;
|
else if constexpr (mode == query_t::min_viable)
|
||||||
|
res = query_min(v, result);
|
||||||
|
else if constexpr (mode == query_t::max_viable)
|
||||||
|
res = query_max(v, result);
|
||||||
|
else if constexpr (mode == query_t::has_viable) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res != l_undef)
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
refinements--;
|
LOG("Refinement budget exhausted! Fall back to univariate solver.");
|
||||||
|
return query_fallback<mode>(v, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_find(pvar v, rational& lo, rational& hi) {
|
||||||
|
auto const& max_value = s.var2pdd(v).max_value();
|
||||||
|
lbool const refined = l_undef;
|
||||||
|
|
||||||
// After a refinement, any of the existing entries may have been replaced
|
// After a refinement, any of the existing entries may have been replaced
|
||||||
// (if it is subsumed by the new entry created during refinement).
|
// (if it is subsumed by the new entry created during refinement).
|
||||||
// For this reason, we start chasing the intervals from the start again.
|
// For this reason, we start chasing the intervals from the start again.
|
||||||
lo = 0;
|
lo = 0;
|
||||||
|
hi = max_value;
|
||||||
|
|
||||||
auto* e = m_units[v];
|
auto* e = m_units[v];
|
||||||
if (!e && !refine_viable(v, lo))
|
if (!e && !refine_viable(v, lo))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!e && !refine_viable(v, rational::one()))
|
if (!e && !refine_viable(v, hi))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!e)
|
if (!e)
|
||||||
return find_t::multiple;
|
return l_true;
|
||||||
if (e->interval.is_full())
|
if (e->interval.is_full()) {
|
||||||
return find_t::empty;
|
s.set_conflict_by_viable_interval(v);
|
||||||
|
return l_false;
|
||||||
|
}
|
||||||
|
|
||||||
entry* first = e;
|
entry* first = e;
|
||||||
entry* last = first->prev();
|
entry* last = first->prev();
|
||||||
|
@ -635,10 +783,10 @@ namespace polysat {
|
||||||
last->interval.hi_val() < max_value) {
|
last->interval.hi_val() < max_value) {
|
||||||
lo = last->interval.hi_val();
|
lo = last->interval.hi_val();
|
||||||
if (!refine_viable(v, lo))
|
if (!refine_viable(v, lo))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!refine_viable(v, max_value))
|
if (!refine_viable(v, max_value))
|
||||||
goto refined;
|
return refined;
|
||||||
return find_t::multiple;
|
return l_true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find lower bound
|
// find lower bound
|
||||||
|
@ -652,8 +800,10 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != first);
|
while (e != first);
|
||||||
|
|
||||||
if (e->interval.currently_contains(lo))
|
if (e->interval.currently_contains(lo)) {
|
||||||
return find_t::empty;
|
s.set_conflict_by_viable_interval(v);
|
||||||
|
return l_false;
|
||||||
|
}
|
||||||
|
|
||||||
// find upper bound
|
// find upper bound
|
||||||
hi = max_value;
|
hi = max_value;
|
||||||
|
@ -666,51 +816,22 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != last);
|
while (e != last);
|
||||||
if (!refine_viable(v, lo))
|
if (!refine_viable(v, lo))
|
||||||
goto refined;
|
return refined;
|
||||||
if (!refine_viable(v, hi))
|
if (!refine_viable(v, hi))
|
||||||
goto refined;
|
return refined;
|
||||||
|
return l_true;
|
||||||
if (lo == hi)
|
|
||||||
return find_t::singleton;
|
|
||||||
else
|
|
||||||
return find_t::multiple;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
find_t viable::find_viable(pvar v, rational& lo) {
|
lbool viable::query_min(pvar v, rational& lo) {
|
||||||
#if 1
|
// TODO: should be able to deal with UNSAT case; since also min_viable has to deal with it due to fallback solver
|
||||||
rational hi;
|
|
||||||
// return query<query_t::find_viable>(v, lo, hi);
|
|
||||||
return query(query_t::find_viable, v, lo, hi);
|
|
||||||
#else
|
|
||||||
refined:
|
|
||||||
lo = 0;
|
lo = 0;
|
||||||
auto* e = m_units[v];
|
entry* e = m_units[v];
|
||||||
if (!e && !refine_viable(v, lo))
|
if (!e && !refine_viable(v, lo))
|
||||||
goto refined;
|
return l_undef;
|
||||||
if (!e && !refine_viable(v, rational::one()))
|
|
||||||
goto refined;
|
|
||||||
if (!e)
|
if (!e)
|
||||||
return find_t::multiple;
|
return l_true;
|
||||||
if (e->interval.is_full())
|
|
||||||
return find_t::empty;
|
|
||||||
|
|
||||||
entry* first = e;
|
entry* first = e;
|
||||||
entry* last = first->prev();
|
entry* last = first->prev();
|
||||||
|
|
||||||
// quick check: last interval does not wrap around
|
|
||||||
// and has space for 2 unassigned values.
|
|
||||||
auto& max_value = s.var2pdd(v).max_value();
|
|
||||||
if (last->interval.lo_val() < last->interval.hi_val() &&
|
|
||||||
last->interval.hi_val() < max_value) {
|
|
||||||
lo = last->interval.hi_val();
|
|
||||||
if (!refine_viable(v, lo))
|
|
||||||
goto refined;
|
|
||||||
if (!refine_viable(v, max_value))
|
|
||||||
goto refined;
|
|
||||||
return find_t::multiple;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find lower bound
|
|
||||||
if (last->interval.currently_contains(lo))
|
if (last->interval.currently_contains(lo))
|
||||||
lo = last->interval.hi_val();
|
lo = last->interval.hi_val();
|
||||||
do {
|
do {
|
||||||
|
@ -720,12 +841,21 @@ namespace polysat {
|
||||||
e = e->next();
|
e = e->next();
|
||||||
}
|
}
|
||||||
while (e != first);
|
while (e != first);
|
||||||
|
if (!refine_viable(v, lo))
|
||||||
|
return l_undef;
|
||||||
|
SASSERT(is_viable(v, lo));
|
||||||
|
return l_true;
|
||||||
|
}
|
||||||
|
|
||||||
if (e->interval.currently_contains(lo))
|
lbool viable::query_max(pvar v, rational& hi) {
|
||||||
return find_t::empty;
|
// TODO: should be able to deal with UNSAT case; since also max_viable has to deal with it due to fallback solver
|
||||||
|
hi = s.var2pdd(v).max_value();
|
||||||
// find upper bound
|
auto* e = m_units[v];
|
||||||
rational hi = max_value;
|
if (!e && !refine_viable(v, hi))
|
||||||
|
return l_undef;
|
||||||
|
if (!e)
|
||||||
|
return l_true;
|
||||||
|
entry* last = e->prev();
|
||||||
e = last;
|
e = last;
|
||||||
do {
|
do {
|
||||||
if (!e->interval.currently_contains(hi))
|
if (!e->interval.currently_contains(hi))
|
||||||
|
@ -734,18 +864,126 @@ namespace polysat {
|
||||||
e = e->prev();
|
e = e->prev();
|
||||||
}
|
}
|
||||||
while (e != last);
|
while (e != last);
|
||||||
if (!refine_viable(v, lo))
|
|
||||||
goto refined;
|
|
||||||
if (!refine_viable(v, hi))
|
if (!refine_viable(v, hi))
|
||||||
goto refined;
|
return l_undef;
|
||||||
if (lo == hi)
|
SASSERT(is_viable(v, hi));
|
||||||
return find_t::singleton;
|
return l_true;
|
||||||
else
|
|
||||||
return find_t::multiple;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool viable::resolve(pvar v, conflict& core) {
|
template <query_t mode>
|
||||||
|
lbool viable::query_fallback(pvar v, typename query_result<mode>::result_t& result) {
|
||||||
|
unsigned const bit_width = s.size(v);
|
||||||
|
univariate_solver* us = s.m_viable_fallback.usolver(bit_width);
|
||||||
|
sat::literal_set added;
|
||||||
|
|
||||||
|
// First step: only query the looping constraints and see if they alone are already UNSAT.
|
||||||
|
// The constraints which caused the refinement loop will be reached from m_units.
|
||||||
|
LOG_H3("Checking looping univariate constraints for v" << v << "...");
|
||||||
|
LOG("Assignment: " << assignments_pp(s));
|
||||||
|
entry const* first = m_units[v];
|
||||||
|
entry const* e = first;
|
||||||
|
do {
|
||||||
|
entry const* origin = e;
|
||||||
|
while (origin->refined)
|
||||||
|
origin = origin->refined;
|
||||||
|
signed_constraint const c = origin->src;
|
||||||
|
sat::literal const lit = c.blit();
|
||||||
|
if (!added.contains(lit)) {
|
||||||
|
added.insert(lit);
|
||||||
|
LOG("Adding " << lit_pp(s, lit));
|
||||||
|
c.add_to_univariate_solver(v, s, *us, lit.to_uint());
|
||||||
|
}
|
||||||
|
e = e->next();
|
||||||
|
}
|
||||||
|
while (e != first);
|
||||||
|
|
||||||
|
switch (us->check()) {
|
||||||
|
case l_false:
|
||||||
|
s.set_conflict_by_viable_fallback(v, *us);
|
||||||
|
return l_false;
|
||||||
|
case l_true:
|
||||||
|
// At this point we don't know much because we did not add all relevant constraints
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// resource limit
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second step: looping constraints aren't UNSAT, so add the remaining relevant constraints
|
||||||
|
LOG_H3("Checking all univariate constraints for v" << v << "...");
|
||||||
|
auto const& cs = s.m_viable_fallback.m_constraints[v];
|
||||||
|
for (unsigned i = cs.size(); i-- > 0; ) {
|
||||||
|
sat::literal const lit = cs[i].blit();
|
||||||
|
if (added.contains(lit))
|
||||||
|
continue;
|
||||||
|
LOG("Adding " << lit_pp(s, lit));
|
||||||
|
added.insert(lit);
|
||||||
|
cs[i].add_to_univariate_solver(v, s, *us, lit.to_uint());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (us->check()) {
|
||||||
|
case l_false:
|
||||||
|
s.set_conflict_by_viable_fallback(v, *us);
|
||||||
|
return l_false;
|
||||||
|
case l_true:
|
||||||
|
// pass solver to mode-specific query
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// resource limit
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::find_viable)
|
||||||
|
return query_find_fallback(v, *us, result.first, result.second);
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::min_viable)
|
||||||
|
return query_min_fallback(v, *us, result);
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::max_viable)
|
||||||
|
return query_max_fallback(v, *us, result);
|
||||||
|
|
||||||
|
if constexpr (mode == query_t::has_viable) {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
UNREACHABLE();
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_find_fallback(pvar v, univariate_solver& us, rational& lo, rational& hi) {
|
||||||
|
return us.find_two(lo, hi) ? l_true : l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_min_fallback(pvar v, univariate_solver& us, rational& lo) {
|
||||||
|
return us.find_min(lo) ? l_true : l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool viable::query_max_fallback(pvar v, univariate_solver& us, rational& hi) {
|
||||||
|
return us.find_max(hi) ? l_true : l_undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool viable::resolve_fallback(pvar v, univariate_solver& us, conflict& core) {
|
||||||
|
// The conflict is the unsat core of the univariate solver,
|
||||||
|
// and the current assignment (under which the constraints are univariate in v)
|
||||||
|
// TODO:
|
||||||
|
// - currently we add variables directly, which is sound:
|
||||||
|
// e.g.: v^2 + w^2 == 0; w := 1
|
||||||
|
// - but we could use side constraints on the coefficients instead (coefficients when viewed as polynomial over v):
|
||||||
|
// e.g.: v^2 + w^2 == 0; w^2 == 1
|
||||||
|
for (unsigned dep : us.unsat_core()) {
|
||||||
|
sat::literal lit = sat::to_literal(dep);
|
||||||
|
signed_constraint c = s.lit2cnstr(lit);
|
||||||
|
core.insert(c);
|
||||||
|
core.insert_vars(c);
|
||||||
|
}
|
||||||
|
SASSERT(!core.vars().contains(v));
|
||||||
|
core.add_lemma("viable unsat core", core.build_lemma());
|
||||||
|
verbose_stream() << "unsat core " << core << "\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool viable::resolve_interval(pvar v, conflict& core) {
|
||||||
DEBUG_CODE( log(v); );
|
DEBUG_CODE( log(v); );
|
||||||
if (has_viable(v))
|
if (has_viable(v))
|
||||||
return false;
|
return false;
|
||||||
|
@ -810,7 +1048,7 @@ namespace polysat {
|
||||||
auto lhs = hi - next_lo;
|
auto lhs = hi - next_lo;
|
||||||
auto rhs = next_hi - next_lo;
|
auto rhs = next_hi - next_lo;
|
||||||
signed_constraint c = s.m_constraints.ult(lhs, rhs);
|
signed_constraint c = s.m_constraints.ult(lhs, rhs);
|
||||||
lemma.insert_eval(~c);
|
lemma.insert_try_eval(~c); // "try" because linking constraint may contain unassigned variables, see test_polysat::test_bench23_fi_lemma for an example.
|
||||||
}
|
}
|
||||||
for (auto sc : e->side_cond)
|
for (auto sc : e->side_cond)
|
||||||
lemma.insert_eval(~sc);
|
lemma.insert_eval(~sc);
|
||||||
|
@ -821,8 +1059,12 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
while (e != first);
|
while (e != first);
|
||||||
|
|
||||||
SASSERT(all_of(lemma, [this](sat::literal lit) { return s.m_bvars.value(lit) == l_false || s.lit2cnstr(lit).is_currently_false(s); }));
|
// Doesn't hold anymore: we may get new constraints with unassigned variables, see test_polysat::test_bench23_fi_lemma.
|
||||||
|
// SASSERT(all_of(lemma, [this](sat::literal lit) { return s.m_bvars.value(lit) == l_false || s.lit2cnstr(lit).is_currently_false(s); }));
|
||||||
|
|
||||||
|
// NSB review: bench23 exposes a scenario where s.m_bvars.value(lit) == l_true. So the viable lemma is mute, but the literal in the premise
|
||||||
|
// is a conflict.
|
||||||
|
// SASSERT(all_of(lemma, [this](sat::literal lit) { return s.m_bvars.value(lit) != l_true; }));
|
||||||
core.add_lemma("viable", lemma.build());
|
core.add_lemma("viable", lemma.build());
|
||||||
core.logger().log(inf_fi(*this, v));
|
core.logger().log(inf_fi(*this, v));
|
||||||
return true;
|
return true;
|
||||||
|
@ -947,27 +1189,36 @@ namespace polysat {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
find_t viable_fallback::find_viable(pvar v, rational& out_val) {
|
univariate_solver* viable_fallback::usolver(unsigned bit_width) {
|
||||||
unsigned bit_width = s.m_size[v];
|
|
||||||
|
|
||||||
univariate_solver* us;
|
univariate_solver* us;
|
||||||
|
|
||||||
auto it = m_usolver.find_iterator(bit_width);
|
auto it = m_usolver.find_iterator(bit_width);
|
||||||
if (it != m_usolver.end()) {
|
if (it != m_usolver.end()) {
|
||||||
us = it->m_value.get();
|
us = it->m_value.get();
|
||||||
us->pop(1);
|
us->pop(1);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
auto& mk_solver = *m_usolver_factory;
|
auto& mk_solver = *m_usolver_factory;
|
||||||
m_usolver.insert(bit_width, mk_solver(bit_width));
|
m_usolver.insert(bit_width, mk_solver(bit_width));
|
||||||
us = m_usolver[bit_width].get();
|
us = m_usolver[bit_width].get();
|
||||||
}
|
}
|
||||||
|
SASSERT_EQ(us->scope_level(), 0);
|
||||||
|
|
||||||
// push once on the empty solver so we can reset it before the next use
|
// push once on the empty solver so we can reset it before the next use
|
||||||
us->push();
|
us->push();
|
||||||
|
|
||||||
|
return us;
|
||||||
|
}
|
||||||
|
|
||||||
|
find_t viable_fallback::find_viable(pvar v, rational& out_val) {
|
||||||
|
unsigned const bit_width = s.m_size[v];
|
||||||
|
univariate_solver* us = usolver(bit_width);
|
||||||
|
|
||||||
auto const& cs = m_constraints[v];
|
auto const& cs = m_constraints[v];
|
||||||
for (unsigned i = cs.size(); i-- > 0; ) {
|
for (unsigned i = cs.size(); i-- > 0; ) {
|
||||||
LOG("Univariate constraint: " << cs[i]);
|
signed_constraint const c = cs[i];
|
||||||
cs[i].add_to_univariate_solver(s, *us, i);
|
LOG("Univariate constraint: " << c);
|
||||||
|
c.add_to_univariate_solver(v, s, *us, c.blit().to_uint());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (us->check()) {
|
switch (us->check()) {
|
||||||
|
@ -976,22 +1227,13 @@ namespace polysat {
|
||||||
// we don't know whether the SMT instance has a unique solution
|
// we don't know whether the SMT instance has a unique solution
|
||||||
return find_t::multiple;
|
return find_t::multiple;
|
||||||
case l_false:
|
case l_false:
|
||||||
|
s.set_conflict_by_viable_fallback(v, *us);
|
||||||
return find_t::empty;
|
return find_t::empty;
|
||||||
default:
|
default:
|
||||||
return find_t::resource_out;
|
return find_t::resource_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signed_constraints viable_fallback::unsat_core(pvar v) {
|
|
||||||
unsigned bit_width = s.m_size[v];
|
|
||||||
SASSERT(m_usolver[bit_width]);
|
|
||||||
signed_constraints cs;
|
|
||||||
for (unsigned dep : m_usolver[bit_width]->unsat_core()) {
|
|
||||||
cs.push_back(m_constraints[v][dep]);
|
|
||||||
}
|
|
||||||
return cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, find_t x) {
|
std::ostream& operator<<(std::ostream& out, find_t x) {
|
||||||
switch (x) {
|
switch (x) {
|
||||||
case find_t::empty:
|
case find_t::empty:
|
||||||
|
|
|
@ -25,6 +25,7 @@ Author:
|
||||||
#include "math/polysat/conflict.h"
|
#include "math/polysat/conflict.h"
|
||||||
#include "math/polysat/constraint.h"
|
#include "math/polysat/constraint.h"
|
||||||
#include "math/polysat/forbidden_intervals.h"
|
#include "math/polysat/forbidden_intervals.h"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
@ -39,6 +40,34 @@ namespace polysat {
|
||||||
resource_out,
|
resource_out,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace viable_query {
|
||||||
|
enum class query_t {
|
||||||
|
has_viable, // currently only used internally in resolve_viable
|
||||||
|
min_viable, // currently unused
|
||||||
|
max_viable, // currently unused
|
||||||
|
find_viable,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <query_t mode>
|
||||||
|
struct query_result {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct query_result<query_t::min_viable> {
|
||||||
|
using result_t = rational;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct query_result<query_t::max_viable> {
|
||||||
|
using result_t = rational;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct query_result<query_t::find_viable> {
|
||||||
|
using result_t = std::pair<rational&, rational&>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, find_t x);
|
std::ostream& operator<<(std::ostream& out, find_t x);
|
||||||
|
|
||||||
class viable {
|
class viable {
|
||||||
|
@ -76,15 +105,36 @@ namespace polysat {
|
||||||
|
|
||||||
void propagate(pvar v, rational const& val);
|
void propagate(pvar v, rational const& val);
|
||||||
|
|
||||||
enum class query_t {
|
/**
|
||||||
has_viable, // currently only used internally in resolve_viable
|
* Interval-based queries
|
||||||
min_viable, // currently unused
|
* @return l_true on success, l_false on conflict, l_undef on refinement
|
||||||
max_viable, // currently unused
|
*/
|
||||||
find_viable,
|
lbool query_min(pvar v, rational& out_lo);
|
||||||
};
|
lbool query_max(pvar v, rational& out_hi);
|
||||||
|
lbool query_find(pvar v, rational& out_lo, rational& out_hi);
|
||||||
|
|
||||||
// template <query_t mode>
|
/**
|
||||||
find_t query(query_t mode, pvar v, rational& out_lo, rational& out_hi);
|
* Bitblasting-based queries.
|
||||||
|
* The univariate solver has already been filled with all relevant constraints and check() returned l_true.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
lbool query_min_fallback(pvar v, univariate_solver& us, rational& out_lo);
|
||||||
|
lbool query_max_fallback(pvar v, univariate_solver& us, rational& out_hi);
|
||||||
|
lbool query_find_fallback(pvar v, univariate_solver& us, rational& out_lo, rational& out_hi);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interval-based query with bounded refinement and fallback to bitblasting.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
template <viable_query::query_t mode>
|
||||||
|
lbool query(pvar v, typename viable_query::query_result<mode>::result_t& out_result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitblasting-based query.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
template <viable_query::query_t mode>
|
||||||
|
lbool query_fallback(pvar v, typename viable_query::query_result<mode>::result_t& out_result);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
viable(solver& s);
|
viable(solver& s);
|
||||||
|
@ -122,23 +172,54 @@ namespace polysat {
|
||||||
*/
|
*/
|
||||||
bool is_viable(pvar v, rational const& val);
|
bool is_viable(pvar v, rational const& val);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Extract min and max viable values for v
|
* Extract min viable value for v.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
*/
|
*/
|
||||||
rational min_viable(pvar v);
|
lbool min_viable(pvar v, rational& out_lo);
|
||||||
rational max_viable(pvar v);
|
|
||||||
|
/**
|
||||||
|
* Extract max viable value for v.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
lbool max_viable(pvar v, rational& out_hi);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for an upper bound literal for v together with justification.
|
||||||
|
* @return true if a non-trivial upper bound is found, return justifying constraint.
|
||||||
|
*/
|
||||||
|
bool has_upper_bound(pvar v, rational& out_hi, vector<signed_constraint>& out_c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for an lower bound literal for v together with justification.
|
||||||
|
* @return true if a non-trivial lower bound is found, return justifying constraint.
|
||||||
|
*/
|
||||||
|
bool has_lower_bound(pvar v, rational& out_lo, vector<signed_constraint>& out_c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a next viable value for variable.
|
* Find a next viable value for variable.
|
||||||
*/
|
*/
|
||||||
find_t find_viable(pvar v, rational& out_val);
|
find_t find_viable(pvar v, rational& out_val);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a next viable value for variable. Attempts to find two different values, to distinguish propagation/decision.
|
||||||
|
* @return l_true on success, l_false on conflict, l_undef on resource limit
|
||||||
|
*/
|
||||||
|
lbool find_viable(pvar v, rational& out_lo, rational& out_hi);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the unsat core for v,
|
* Retrieve the unsat core for v,
|
||||||
* and add the forbidden interval lemma for v (which eliminates v from the unsat core).
|
* and add the forbidden interval lemma for v (which eliminates v from the unsat core).
|
||||||
* \pre there are no viable values for v
|
* \pre there are no viable values for v (determined by interval reasoning)
|
||||||
*/
|
*/
|
||||||
bool resolve(pvar v, conflict& core);
|
bool resolve_interval(pvar v, conflict& core);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the unsat core for v.
|
||||||
|
* \pre there are no viable values for v (determined by fallback solver)
|
||||||
|
*/
|
||||||
|
bool resolve_fallback(pvar v, univariate_solver& us, conflict& core);
|
||||||
|
|
||||||
/** Log all viable values for the given variable.
|
/** Log all viable values for the given variable.
|
||||||
* (Inefficient, but useful for debugging small instances.)
|
* (Inefficient, but useful for debugging small instances.)
|
||||||
|
@ -251,6 +332,8 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
class viable_fallback {
|
class viable_fallback {
|
||||||
|
friend class viable;
|
||||||
|
|
||||||
solver& s;
|
solver& s;
|
||||||
|
|
||||||
scoped_ptr<univariate_solver_factory> m_usolver_factory;
|
scoped_ptr<univariate_solver_factory> m_usolver_factory;
|
||||||
|
@ -258,6 +341,8 @@ namespace polysat {
|
||||||
vector<signed_constraints> m_constraints;
|
vector<signed_constraints> m_constraints;
|
||||||
svector<unsigned> m_constraints_trail;
|
svector<unsigned> m_constraints_trail;
|
||||||
|
|
||||||
|
univariate_solver* usolver(unsigned bit_width);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
viable_fallback(solver& s);
|
viable_fallback(solver& s);
|
||||||
|
|
||||||
|
@ -275,7 +360,6 @@ namespace polysat {
|
||||||
signed_constraint find_violated_constraint(assignment const& a, pvar v);
|
signed_constraint find_violated_constraint(assignment const& a, pvar v);
|
||||||
|
|
||||||
find_t find_viable(pvar v, rational& out_val);
|
find_t find_viable(pvar v, rational& out_val);
|
||||||
signed_constraints unsat_core(pvar v);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,8 +626,46 @@ namespace polysat {
|
||||||
cb.insert(s.ule(u, v));
|
cb.insert(s.ule(u, v));
|
||||||
auto cl = cb.build();
|
auto cl = cb.build();
|
||||||
simp.apply(*cl);
|
simp.apply(*cl);
|
||||||
std::cout << *cl << "\n";
|
VERIFY_EQ(cl->size(), 2);
|
||||||
SASSERT(cl->size() == 2);
|
}
|
||||||
|
|
||||||
|
// p <= q
|
||||||
|
// p == q (should be removed)
|
||||||
|
static void test_clause_simplify2() {
|
||||||
|
scoped_solver s(__func__);
|
||||||
|
simplify_clause simp(s);
|
||||||
|
clause_builder cb(s);
|
||||||
|
auto u = s.var(s.add_var(32));
|
||||||
|
auto v = s.var(s.add_var(32));
|
||||||
|
auto w = s.var(s.add_var(32));
|
||||||
|
auto p = 2*u*v;
|
||||||
|
auto q = 7*v*w;
|
||||||
|
cb.insert(s.ule(p, q));
|
||||||
|
cb.insert(s.eq(p, q));
|
||||||
|
auto cl = cb.build();
|
||||||
|
simp.apply(*cl);
|
||||||
|
VERIFY_EQ(cl->size(), 1);
|
||||||
|
VERIFY_EQ((*cl)[0], s.ule(p, q).blit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// p < q
|
||||||
|
// p == q
|
||||||
|
// should be merged into p <= q
|
||||||
|
static void test_clause_simplify3() {
|
||||||
|
scoped_solver s(__func__);
|
||||||
|
simplify_clause simp(s);
|
||||||
|
clause_builder cb(s);
|
||||||
|
auto u = s.var(s.add_var(32));
|
||||||
|
auto v = s.var(s.add_var(32));
|
||||||
|
auto w = s.var(s.add_var(32));
|
||||||
|
auto p = 2*u*v;
|
||||||
|
auto q = 7*v*w;
|
||||||
|
cb.insert(s.ult(p, q));
|
||||||
|
cb.insert(s.eq(p, q));
|
||||||
|
auto cl = cb.build();
|
||||||
|
simp.apply(*cl);
|
||||||
|
VERIFY_EQ(cl->size(), 1);
|
||||||
|
VERIFY_EQ((*cl)[0], s.ule(p, q).blit());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8 * x + 3 == 0 or 8 * x + 5 == 0 is unsat
|
// 8 * x + 3 == 0 or 8 * x + 5 == 0 is unsat
|
||||||
|
@ -1099,6 +1137,7 @@ namespace polysat {
|
||||||
if (lemmas.size() == cnt)
|
if (lemmas.size() == cnt)
|
||||||
return;
|
return;
|
||||||
LOG_H1("FAIL: Expected " << cnt << " learned lemmas; got " << lemmas.size() << "!");
|
LOG_H1("FAIL: Expected " << cnt << " learned lemmas; got " << lemmas.size() << "!");
|
||||||
|
SASSERT(false);
|
||||||
if (!collect_test_records)
|
if (!collect_test_records)
|
||||||
VERIFY(false);
|
VERIFY(false);
|
||||||
}
|
}
|
||||||
|
@ -1115,6 +1154,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_H1("FAIL: Lemma " << c1 << " not deduced!");
|
LOG_H1("FAIL: Lemma " << c1 << " not deduced!");
|
||||||
|
SASSERT(false);
|
||||||
if (!collect_test_records)
|
if (!collect_test_records)
|
||||||
VERIFY(false);
|
VERIFY(false);
|
||||||
}
|
}
|
||||||
|
@ -1361,7 +1401,7 @@ namespace polysat {
|
||||||
|
|
||||||
static void test_ineq_non_axiom1(unsigned bw, unsigned i) {
|
static void test_ineq_non_axiom1(unsigned bw, unsigned i) {
|
||||||
auto const bound = rational::power_of_two(bw - 1);
|
auto const bound = rational::power_of_two(bw - 1);
|
||||||
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(i));
|
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||||
auto x = s.var(s.add_var(bw));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto z = s.var(s.add_var(bw));
|
auto z = s.var(s.add_var(bw));
|
||||||
|
@ -1383,7 +1423,7 @@ namespace polysat {
|
||||||
static void test_ineq_axiom2(unsigned bw = 32) {
|
static void test_ineq_axiom2(unsigned bw = 32) {
|
||||||
auto const bound = rational::power_of_two(bw/2);
|
auto const bound = rational::power_of_two(bw/2);
|
||||||
for (unsigned i = 0; i < 6; ++i) {
|
for (unsigned i = 0; i < 6; ++i) {
|
||||||
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(i));
|
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||||
auto x = s.var(s.add_var(bw));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto z = s.var(s.add_var(bw));
|
auto z = s.var(s.add_var(bw));
|
||||||
|
@ -1399,30 +1439,33 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
// xy < b & a <= x & !Omega(x*y) => a*y < b
|
// xy < b & a <= x & !Omega(x*y) => a*y < b
|
||||||
static void test_ineq_axiom3(unsigned bw = 32) {
|
static void test_ineq_axiom3(unsigned bw, unsigned i) {
|
||||||
auto const bound = rational::power_of_two(bw/2);
|
auto const bound = rational::power_of_two(bw/2);
|
||||||
for (unsigned i = 0; i < 24; ++i) {
|
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||||
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(i));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto x = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto a = s.var(s.add_var(bw));
|
||||||
auto a = s.var(s.add_var(bw));
|
auto b = s.var(s.add_var(bw));
|
||||||
auto b = s.var(s.add_var(bw));
|
permute_args(i, x, y, a, b);
|
||||||
permute_args(i, x, y, a, b);
|
s.add_ult(x * y, b);
|
||||||
s.add_ult(x * y, b);
|
s.add_ule(a, x);
|
||||||
s.add_ule(a, x);
|
s.add_ult(x, bound);
|
||||||
s.add_ult(x, bound);
|
s.add_ult(y, bound);
|
||||||
s.add_ult(y, bound);
|
s.add_ule(b, a * y);
|
||||||
s.add_ule(b, a * y);
|
s.check();
|
||||||
s.check();
|
s.expect_unsat();
|
||||||
s.expect_unsat();
|
}
|
||||||
}
|
|
||||||
|
static void test_ineq_axiom3(unsigned bw = 32) {
|
||||||
|
for (unsigned i = 0; i < 24; ++i)
|
||||||
|
test_ineq_axiom3(bw, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// x*y <= b & a <= x & !Omega(x*y) => a*y <= b
|
// x*y <= b & a <= x & !Omega(x*y) => a*y <= b
|
||||||
static void test_ineq_axiom4(unsigned bw = 32) {
|
static void test_ineq_axiom4(unsigned bw = 32) {
|
||||||
auto const bound = rational::power_of_two(bw/2);
|
auto const bound = rational::power_of_two(bw/2);
|
||||||
for (unsigned i = 0; i < 24; ++i) {
|
for (unsigned i = 0; i < 24; ++i) {
|
||||||
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(i));
|
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||||
auto x = s.var(s.add_var(bw));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto a = s.var(s.add_var(bw));
|
auto a = s.var(s.add_var(bw));
|
||||||
|
@ -1465,7 +1508,7 @@ namespace polysat {
|
||||||
// a < xy & x <= b & !Omega(b*y) => a < b*y
|
// a < xy & x <= b & !Omega(b*y) => a < b*y
|
||||||
static void test_ineq_axiom5(unsigned bw, unsigned i) {
|
static void test_ineq_axiom5(unsigned bw, unsigned i) {
|
||||||
auto const bound = rational::power_of_two(bw/2);
|
auto const bound = rational::power_of_two(bw/2);
|
||||||
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(i));
|
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||||
auto x = s.var(s.add_var(bw));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto a = s.var(s.add_var(bw));
|
auto a = s.var(s.add_var(bw));
|
||||||
|
@ -1488,7 +1531,7 @@ namespace polysat {
|
||||||
// a <= xy & x <= b & !Omega(b*y) => a <= b*y
|
// a <= xy & x <= b & !Omega(b*y) => a <= b*y
|
||||||
static void test_ineq_axiom6(unsigned bw, unsigned i) {
|
static void test_ineq_axiom6(unsigned bw, unsigned i) {
|
||||||
auto const bound = rational::power_of_two(bw/2);
|
auto const bound = rational::power_of_two(bw/2);
|
||||||
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(i));
|
scoped_solver s(concat(__func__, " bw=", bw, " perm=", i));
|
||||||
auto x = s.var(s.add_var(bw));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto a = s.var(s.add_var(bw));
|
auto a = s.var(s.add_var(bw));
|
||||||
|
@ -1757,6 +1800,29 @@ namespace polysat {
|
||||||
s.expect_sat({{a, 5}});
|
s.expect_sat({{a, 5}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Motivated by weak forbidden intervals lemma in bench23:
|
||||||
|
* v66 > v41 && v67 == v66 ==> v67 != 0
|
||||||
|
*
|
||||||
|
* Cause by omitting v41 from the symbolic bounds of v66 > v41.
|
||||||
|
*
|
||||||
|
* The stronger lemma should be:
|
||||||
|
* v66 > v41 && v67 == v66 ==> v41 + 1 <= v67
|
||||||
|
*
|
||||||
|
* The conclusion could also be v67 > v41 (note that -1 > v41 follows from premise v66 > v41, so both conclusions are equivalent in this lemma).
|
||||||
|
*/
|
||||||
|
static void test_bench23_fi_lemma() {
|
||||||
|
scoped_solver s(__func__);
|
||||||
|
auto x = s.var(s.add_var(256)); // v41
|
||||||
|
auto y = s.var(s.add_var(256)); // v66
|
||||||
|
auto z = s.var(s.add_var(256)); // v67
|
||||||
|
s.add_ult(x, y); // v66 > v41
|
||||||
|
s.add_eq(y, z); // v66 == v67
|
||||||
|
s.add_eq(z, 0); // v67 == 0
|
||||||
|
s.check();
|
||||||
|
s.expect_unsat();
|
||||||
|
}
|
||||||
|
|
||||||
static void test_bench6_viable() {
|
static void test_bench6_viable() {
|
||||||
scoped_solver s(__func__);
|
scoped_solver s(__func__);
|
||||||
rational coeff("12737129816104781496592808350955669863859698313220462044340334240870444260393");
|
rational coeff("12737129816104781496592808350955669863859698313220462044340334240870444260393");
|
||||||
|
@ -1768,6 +1834,66 @@ namespace polysat {
|
||||||
s.check();
|
s.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_bench27_viable(const char* coeff) {
|
||||||
|
scoped_solver s(__func__);
|
||||||
|
rational a(coeff);
|
||||||
|
rational b = rational::power_of_two(128) - 1;
|
||||||
|
auto x = s.var(s.add_var(256));
|
||||||
|
s.add_ult(0, x);
|
||||||
|
s.add_ule(a * x + b, b);
|
||||||
|
s.check();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_bench27_viable1() {
|
||||||
|
test_bench27_viable("2787252867449406954228501409473613420036128"); // 2^5 * a'
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_bench27_viable2() {
|
||||||
|
test_bench27_viable("5574846017265734846920466193554658608284160"); // 2^9 * a'
|
||||||
|
}
|
||||||
|
|
||||||
|
// -1*v12 <= -1*v12 + v8 + v7*v1 + 2^128*v7 + 1
|
||||||
|
// v8 := 0 v12 := 1 v4 := 0 v9 := 1 v17 := 0 v1 := 5574846017265734846920466193554658608283648
|
||||||
|
//
|
||||||
|
// Substitute assignment:
|
||||||
|
// -1 <= (5574846017265734846920466193554658608283648 + 2^128) * v7
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ == 2^142
|
||||||
|
// this is actually an equation
|
||||||
|
//
|
||||||
|
// 2^142 * v7 == -1, unsatisfiable due to parity
|
||||||
|
static void test_bench27_viable3() {
|
||||||
|
scoped_solver s(__func__);
|
||||||
|
rational const a("5574846017265734846920466193554658608283648");
|
||||||
|
rational const b = rational::power_of_two(128);
|
||||||
|
auto const v1 = s.var(s.add_var(256));
|
||||||
|
auto const v7 = s.var(s.add_var(256));
|
||||||
|
auto const v8 = s.var(s.add_var(256));
|
||||||
|
auto const v12 = s.var(s.add_var(256));
|
||||||
|
s.add_ule(-v12, -v12 + v8 + v7*v1 + b*v7 + 1);
|
||||||
|
s.add_eq(v8, 0);
|
||||||
|
s.add_eq(v12, 1);
|
||||||
|
s.add_eq(v1, a);
|
||||||
|
s.check();
|
||||||
|
s.expect_unsat();
|
||||||
|
}
|
||||||
|
|
||||||
|
// similar as test_bench27_viable3, but satisfiable (to test the other branch)
|
||||||
|
static void test_bench27_viable3_sat() {
|
||||||
|
scoped_solver s(__func__);
|
||||||
|
rational const a("5574846017265734846920466193554658608283648");
|
||||||
|
rational const b = rational::power_of_two(128) - 1;
|
||||||
|
auto const v1 = s.var(s.add_var(256));
|
||||||
|
auto const v7 = s.var(s.add_var(256));
|
||||||
|
auto const v8 = s.var(s.add_var(256));
|
||||||
|
auto const v12 = s.var(s.add_var(256));
|
||||||
|
s.add_ule(-v12, -v12 + v8 + v7*v1 + b*v7 + 1);
|
||||||
|
s.add_eq(v8, 0);
|
||||||
|
s.add_eq(v12, 1);
|
||||||
|
s.add_eq(v1, a);
|
||||||
|
s.check();
|
||||||
|
s.expect_sat();
|
||||||
|
}
|
||||||
|
|
||||||
}; // class test_polysat
|
}; // class test_polysat
|
||||||
|
|
||||||
|
|
||||||
|
@ -1898,23 +2024,15 @@ static void STD_CALL polysat_on_ctrl_c(int) {
|
||||||
|
|
||||||
void tst_polysat() {
|
void tst_polysat() {
|
||||||
using namespace polysat;
|
using namespace polysat;
|
||||||
polysat::test_polysat::test_band6(4);
|
|
||||||
|
|
||||||
#if 0 // Enable this block to run a single unit test with detailed output.
|
#if 0 // Enable this block to run a single unit test with detailed output.
|
||||||
collect_test_records = false;
|
collect_test_records = false;
|
||||||
test_max_conflicts = 50;
|
test_max_conflicts = 50;
|
||||||
// test_polysat::test_parity1();
|
test_polysat::test_ineq_axiom3(32, 3); // TODO: assertion
|
||||||
// test_polysat::test_parity1b();
|
// test_polysat::test_ineq_axiom6(32, 0); // TODO: assertion
|
||||||
// test_polysat::test_parity2();
|
// test_polysat::test_band5(); // TODO: assertion when clause simplification (merging p>q and p=q) is enabled
|
||||||
// test_polysat::test_parity3();
|
// test_polysat::test_bench27_viable1(); // TODO: refinement
|
||||||
// test_polysat::test_parity4();
|
// test_polysat::test_bench27_viable2(); // TODO: refinement
|
||||||
// test_polysat::test_parity4(32);
|
|
||||||
test_polysat::test_parity4(256);
|
|
||||||
// test_polysat::test_ineq2();
|
|
||||||
// test_polysat::test_ineq_non_axiom4(32, 3); // stuck in viable refinement loop
|
|
||||||
// test_polysat::test_ineq_non_axiom4(32, 4); // stuck in viable refinement loop
|
|
||||||
// test_polysat::test_band1();
|
|
||||||
// test_polysat::test_bench6_viable();
|
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1930,6 +2048,16 @@ void tst_polysat() {
|
||||||
set_log_enabled(false);
|
set_log_enabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
RUN(test_polysat::test_elim1());
|
||||||
|
RUN(test_polysat::test_elim2());
|
||||||
|
RUN(test_polysat::test_elim3());
|
||||||
|
RUN(test_polysat::test_elim4());
|
||||||
|
RUN(test_polysat::test_elim5());
|
||||||
|
RUN(test_polysat::test_elim6());
|
||||||
|
RUN(test_polysat::test_elim7());
|
||||||
|
#endif
|
||||||
|
|
||||||
RUN(test_polysat::test_parity1());
|
RUN(test_polysat::test_parity1());
|
||||||
RUN(test_polysat::test_parity1b());
|
RUN(test_polysat::test_parity1b());
|
||||||
RUN(test_polysat::test_parity2());
|
RUN(test_polysat::test_parity2());
|
||||||
|
@ -1937,6 +2065,9 @@ void tst_polysat() {
|
||||||
RUN(test_polysat::test_parity4());
|
RUN(test_polysat::test_parity4());
|
||||||
|
|
||||||
RUN(test_polysat::test_clause_simplify1());
|
RUN(test_polysat::test_clause_simplify1());
|
||||||
|
RUN(test_polysat::test_clause_simplify2());
|
||||||
|
// RUN(test_polysat::test_clause_simplify3()); // TODO: corresponding simplification is disabled at the moment
|
||||||
|
RUN(test_polysat::test_bench23_fi_lemma());
|
||||||
|
|
||||||
RUN(test_polysat::test_add_conflicts());
|
RUN(test_polysat::test_add_conflicts());
|
||||||
RUN(test_polysat::test_wlist());
|
RUN(test_polysat::test_wlist());
|
||||||
|
@ -1966,14 +2097,6 @@ void tst_polysat() {
|
||||||
|
|
||||||
RUN(test_polysat::test_var_minimize()); // works but var_minimize isn't used (UNSAT before lemma is created)
|
RUN(test_polysat::test_var_minimize()); // works but var_minimize isn't used (UNSAT before lemma is created)
|
||||||
|
|
||||||
RUN(test_polysat::test_elim1());
|
|
||||||
RUN(test_polysat::test_elim2());
|
|
||||||
RUN(test_polysat::test_elim3());
|
|
||||||
RUN(test_polysat::test_elim4());
|
|
||||||
RUN(test_polysat::test_elim5());
|
|
||||||
RUN(test_polysat::test_elim6());
|
|
||||||
RUN(test_polysat::test_elim7());
|
|
||||||
|
|
||||||
RUN(test_polysat::test_ineq1());
|
RUN(test_polysat::test_ineq1());
|
||||||
RUN(test_polysat::test_ineq2());
|
RUN(test_polysat::test_ineq2());
|
||||||
RUN(test_polysat::test_monot());
|
RUN(test_polysat::test_monot());
|
||||||
|
@ -2002,6 +2125,10 @@ void tst_polysat() {
|
||||||
RUN(test_polysat::test_fi_nonmax());
|
RUN(test_polysat::test_fi_nonmax());
|
||||||
RUN(test_polysat::test_fi_disequal_mild());
|
RUN(test_polysat::test_fi_disequal_mild());
|
||||||
RUN(test_polysat::test_bench6_viable());
|
RUN(test_polysat::test_bench6_viable());
|
||||||
|
// RUN(test_polysat::test_bench27_viable1()); // stuck in refinement loop + fallback solver
|
||||||
|
// RUN(test_polysat::test_bench27_viable2()); // stuck in refinement loop + fallback solver
|
||||||
|
RUN(test_polysat::test_bench27_viable3());
|
||||||
|
RUN(test_polysat::test_bench27_viable3_sat());
|
||||||
|
|
||||||
RUN(test_polysat::test_ineq_axiom1());
|
RUN(test_polysat::test_ineq_axiom1());
|
||||||
RUN(test_polysat::test_ineq_axiom2());
|
RUN(test_polysat::test_ineq_axiom2());
|
||||||
|
@ -2010,7 +2137,7 @@ void tst_polysat() {
|
||||||
RUN(test_polysat::test_ineq_axiom5());
|
RUN(test_polysat::test_ineq_axiom5());
|
||||||
RUN(test_polysat::test_ineq_axiom6());
|
RUN(test_polysat::test_ineq_axiom6());
|
||||||
RUN(test_polysat::test_ineq_non_axiom1());
|
RUN(test_polysat::test_ineq_non_axiom1());
|
||||||
//RUN(test_polysat::test_ineq_non_axiom4());
|
RUN(test_polysat::test_ineq_non_axiom4());
|
||||||
|
|
||||||
// test_fi::exhaustive();
|
// test_fi::exhaustive();
|
||||||
// test_fi::randomized();
|
// test_fi::randomized();
|
||||||
|
|
|
@ -38,16 +38,19 @@ namespace polysat {
|
||||||
auto c = s.ule(x + 3, x + 5);
|
auto c = s.ule(x + 3, x + 5);
|
||||||
s.v.intersect(xv, c);
|
s.v.intersect(xv, c);
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << "min-max " << s.v.min_viable(xv) << " " << s.v.max_viable(xv) << "\n";
|
std::cout << "min " << s.v.min_viable(xv, val) << " " << val << "\n";
|
||||||
|
std::cout << "max " << s.v.max_viable(xv, val) << " " << val << "\n";
|
||||||
s.v.intersect(xv, s.ule(x, 2));
|
s.v.intersect(xv, s.ule(x, 2));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
s.v.intersect(xv, s.ule(1, x));
|
s.v.intersect(xv, s.ule(1, x));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << "min-max " << s.v.min_viable(xv) << " " << s.v.max_viable(xv) << "\n";
|
std::cout << "min " << s.v.min_viable(xv, val) << " " << val << "\n";
|
||||||
|
std::cout << "max " << s.v.max_viable(xv, val) << " " << val << "\n";
|
||||||
s.v.intersect(xv, s.ule(x, 3));
|
s.v.intersect(xv, s.ule(x, 3));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
||||||
std::cout << "min-max " << s.v.min_viable(xv) << " " << s.v.max_viable(xv) << "\n";
|
std::cout << "min " << s.v.min_viable(xv, val) << " " << val << "\n";
|
||||||
|
std::cout << "max " << s.v.max_viable(xv, val) << " " << val << "\n";
|
||||||
s.v.intersect(xv, s.ule(3, x));
|
s.v.intersect(xv, s.ule(3, x));
|
||||||
std::cout << s.v << "\n";
|
std::cout << s.v << "\n";
|
||||||
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
std::cout << s.v.find_viable(xv, val) << " " << val << "\n";
|
||||||
|
|
|
@ -490,6 +490,8 @@ public:
|
||||||
|
|
||||||
void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz & d) { mpz_manager<SYNCH>::machine_div_rem(a, b, c, d); }
|
void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz & d) { mpz_manager<SYNCH>::machine_div_rem(a, b, c, d); }
|
||||||
|
|
||||||
|
void machine_div2k(mpz const & a, unsigned k, mpz & c) { mpz_manager<SYNCH>::machine_div2k(a, k, c); }
|
||||||
|
|
||||||
void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::div(a, b, c); }
|
void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager<SYNCH>::div(a, b, c); }
|
||||||
|
|
||||||
void rat_div(mpz const & a, mpz const & b, mpq & c) {
|
void rat_div(mpz const & a, mpz const & b, mpq & c) {
|
||||||
|
@ -516,6 +518,12 @@ public:
|
||||||
machine_div(a.m_num, b.m_num, c);
|
machine_div(a.m_num, b.m_num, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void machine_idiv2k(mpq const & a, unsigned k, mpq & c) {
|
||||||
|
SASSERT(is_int(a));
|
||||||
|
machine_div2k(a.m_num, k, c.m_num);
|
||||||
|
reset_denominator(c);
|
||||||
|
}
|
||||||
|
|
||||||
void idiv(mpq const & a, mpq const & b, mpq & c) {
|
void idiv(mpq const & a, mpq const & b, mpq & c) {
|
||||||
SASSERT(is_int(a) && is_int(b));
|
SASSERT(is_int(a) && is_int(b));
|
||||||
div(a.m_num, b.m_num, c.m_num);
|
div(a.m_num, b.m_num, c.m_num);
|
||||||
|
|
|
@ -153,3 +153,21 @@ bool rational::mult_inverse(unsigned num_bits, rational & result) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute multiplicative pseudo-inverse modulo 2^num_bits:
|
||||||
|
*
|
||||||
|
* mod(n * n.pseudo_inverse(bits), 2^bits) == 2^k,
|
||||||
|
* where k is maximal such that 2^k divides n.
|
||||||
|
*
|
||||||
|
* Precondition: number is non-zero.
|
||||||
|
*/
|
||||||
|
rational rational::pseudo_inverse(unsigned num_bits) const {
|
||||||
|
rational result;
|
||||||
|
rational const& n = *this;
|
||||||
|
SASSERT(!n.is_zero()); // TODO: or we define pseudo-inverse of 0 as 0.
|
||||||
|
unsigned const k = n.trailing_zeros();
|
||||||
|
rational const odd = machine_div2k(n, k);
|
||||||
|
VERIFY(odd.mult_inverse(num_bits, result));
|
||||||
|
SASSERT_EQ(mod(n * result, rational::power_of_two(num_bits)), rational::power_of_two(k));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -229,6 +229,12 @@ public:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend inline rational machine_div2k(rational const & r1, unsigned k) {
|
||||||
|
rational r;
|
||||||
|
rational::m().machine_idiv2k(r1.m_val, k, r.m_val);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
friend inline rational mod(rational const & r1, rational const & r2) {
|
friend inline rational mod(rational const & r1, rational const & r2) {
|
||||||
rational r;
|
rational r;
|
||||||
rational::m().mod(r1.m_val, r2.m_val, r.m_val);
|
rational::m().mod(r1.m_val, r2.m_val, r.m_val);
|
||||||
|
@ -355,6 +361,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mult_inverse(unsigned num_bits, rational & result) const;
|
bool mult_inverse(unsigned num_bits, rational & result) const;
|
||||||
|
rational pseudo_inverse(unsigned num_bits) const;
|
||||||
|
|
||||||
static rational const & zero() {
|
static rational const & zero() {
|
||||||
return m_zero;
|
return m_zero;
|
||||||
|
|
|
@ -89,6 +89,10 @@ public:
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using const_iterator = decltype(m_queue)::const_iterator;
|
||||||
|
const_iterator begin() const { return m_queue.begin(); }
|
||||||
|
const_iterator end() const { return m_queue.end(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, var_queue const& queue) {
|
inline std::ostream& operator<<(std::ostream& out, var_queue const& queue) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue