3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 09:35:32 +00:00

Refactor assignment and search state

This commit is contained in:
Jakob Rath 2022-11-21 17:25:15 +01:00
parent 022c06f75d
commit 6e72a97727
22 changed files with 326 additions and 211 deletions

View file

@ -1,5 +1,6 @@
z3_add_component(polysat
SOURCES
assignment.cpp
boolean.cpp
clause.cpp
clause_builder.cpp

View file

@ -0,0 +1,107 @@
/*++
Copyright (c) 2021 Microsoft Corporation
Module Name:
polysat substitution and assignment
Author:
Nikolaj Bjorner (nbjorner) 2021-03-19
Jakob Rath 2021-04-06
--*/
#include "math/polysat/assignment.h"
#include "math/polysat/solver.h"
namespace polysat {
substitution::substitution(pdd p)
: m_subst(std::move(p)) { }
substitution::substitution(dd::pdd_manager& m)
: m_subst(m.one()) { }
substitution substitution::add(pvar var, rational const& value) const {
return {m_subst.subst_add(var, value)};
}
pdd substitution::apply_to(pdd const& p) const {
return p.subst_val(m_subst);
}
bool substitution::value(pvar var, rational& out_value) const {
return m_subst.subst_get(var, out_value);
}
assignment::assignment(solver& s)
: m_solver(&s) { }
assignment assignment::clone() const {
assignment a(s());
a.m_pairs = m_pairs;
a.m_subst.reserve(m_subst.size());
for (unsigned i = m_subst.size(); i-- > 0; )
if (m_subst[i])
a.m_subst.set(i, alloc(substitution, *m_subst[i]));
a.m_subst_trail = m_subst_trail;
return a;
}
substitution& assignment::subst(unsigned sz) {
return const_cast<substitution&>(std::as_const(*this).subst(sz));
}
substitution const& assignment::subst(unsigned sz) const {
m_subst.reserve(sz + 1);
if (!m_subst[sz])
m_subst.set(sz, alloc(substitution, s().sz2pdd(sz)));
return *m_subst[sz];
}
void assignment::push(pvar var, rational const& value) {
SASSERT(all_of(m_pairs, [var](assignment_item_t const& item) { return item.first != var; }));
m_pairs.push_back({var, value});
unsigned const sz = s().size(var);
substitution& sub = subst(sz);
m_subst_trail.push_back(sub);
sub = sub.add(var, value);
SASSERT_EQ(sub, *m_subst[sz]);
}
void assignment::pop() {
substitution& sub = m_subst_trail.back();
unsigned sz = sub.bit_width();
SASSERT_EQ(sz, s().size(m_pairs.back().first));
*m_subst[sz] = sub;
m_subst_trail.pop_back();
m_pairs.pop_back();
}
pdd assignment::apply_to(pdd const& p) const {
unsigned const sz = p.power_of_2();
return subst(sz).apply_to(p);
}
std::ostream& substitution::display(std::ostream& out) const {
char const* delim = "";
pdd p = m_subst;
while (!p.is_val()) {
SASSERT(p.lo().is_val());
out << delim << "v" << p.var() << " := " << p.lo();
delim = " ";
p = p.hi();
}
return out;
}
std::ostream& assignment::display(std::ostream& out) const {
char const* delim = "";
for (auto const& [var, value] : m_pairs) {
out << delim << assignment_pp(s(), var, value);
delim = " ";
}
return out;
}
}

View file

@ -0,0 +1,114 @@
/*++
Copyright (c) 2021 Microsoft Corporation
Module Name:
polysat substitution and assignment
Author:
Nikolaj Bjorner (nbjorner) 2021-03-19
Jakob Rath 2021-04-06
--*/
#pragma once
#include "math/polysat/types.h"
namespace polysat {
using assignment_item_t = std::pair<pvar, rational>;
class substitution_iterator {
pdd m_current;
substitution_iterator(pdd current) : m_current(std::move(current)) {}
friend class substitution;
public:
using value_type = assignment_item_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type const*;
using reference = value_type const&;
using iterator_category = std::input_iterator_tag;
substitution_iterator& operator++() {
SASSERT(!m_current.is_val());
m_current = m_current.hi();
return *this;
}
value_type operator*() const {
SASSERT(!m_current.is_val());
return { m_current.var(), m_current.lo().val() };
}
bool operator==(substitution_iterator const& other) const { return m_current == other.m_current; }
bool operator!=(substitution_iterator const& other) const { return !operator==(other); }
};
/** Substitution for a single bit width. */
class substitution {
pdd m_subst;
substitution(pdd p);
public:
substitution(dd::pdd_manager& m);
substitution add(pvar var, rational const& value) const;
pdd apply_to(pdd const& p) const;
bool value(pvar var, rational& out_value) const;
bool empty() const { return m_subst.is_one(); }
pdd const& to_pdd() const { return m_subst; }
unsigned bit_width() const { return to_pdd().power_of_2(); }
bool operator==(substitution const& other) const { return &m_subst.manager() == &other.m_subst.manager() && m_subst == other.m_subst; }
bool operator!=(substitution const& other) const { return !operator==(other); }
std::ostream& display(std::ostream& out) const;
using const_iterator = substitution_iterator;
const_iterator begin() const { return {m_subst}; }
const_iterator end() const { return {m_subst.manager().one()}; }
};
/** Full variable assignment, may include variables of varying bit widths. */
class assignment {
solver* m_solver;
vector<assignment_item_t> m_pairs; // TODO: do we still need this?
mutable scoped_ptr_vector<substitution> m_subst;
vector<substitution> m_subst_trail;
substitution& subst(unsigned sz);
solver& s() const { return *m_solver; }
public:
assignment(solver& s);
// prevent implicit copy, use clone() if you do need a copy
assignment(assignment const&) = delete;
assignment& operator=(assignment const&) = delete;
assignment(assignment&&) = default;
assignment& operator=(assignment&&) = default;
assignment clone() const;
void push(pvar var, rational const& value);
void pop();
pdd apply_to(pdd const& p) const;
bool empty() const { return pairs().empty(); }
substitution const& subst(unsigned sz) const;
vector<assignment_item_t> const& pairs() const { return m_pairs; }
using const_iterator = decltype(m_pairs)::const_iterator;
const_iterator begin() const { return pairs().begin(); }
const_iterator end() const { return pairs().end(); }
std::ostream& display(std::ostream& out) const;
};
using assignment_t = assignment;
inline std::ostream& operator<<(std::ostream& out, substitution const& sub) { return sub.display(out); }
inline std::ostream& operator<<(std::ostream& out, assignment const& a) { return a.display(out); }
}

View file

@ -12,12 +12,9 @@ Author:
--*/
#pragma once
#include "math/polysat/types.h"
#include "util/sat_literal.h"
namespace polysat {
class clause;
class bool_var_manager {
enum class kind_t {

View file

@ -24,6 +24,10 @@ Author:
namespace polysat {
bool constraint::is_currently_false(solver& s, bool is_positive) const {
return is_currently_false(s.assignment(), is_positive);
}
bool signed_constraint::is_eq() const {
return is_positive() && m_constraint->is_eq();
}

View file

@ -15,7 +15,7 @@ Author:
#include "math/polysat/boolean.h"
#include "math/polysat/types.h"
#include "math/polysat/interval.h"
#include "math/polysat/search_state.h"
#include "math/polysat/assignment.h"
#include "math/polysat/univariate/univariate_solver.h"
#include <iostream>
@ -85,11 +85,11 @@ namespace polysat {
virtual std::ostream& display(std::ostream& out) const = 0;
virtual bool is_always_false(bool is_positive) const = 0;
virtual bool is_currently_false(solver& s, bool is_positive) const = 0;
virtual bool is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const = 0;
bool is_currently_false(solver& s, bool is_positive) const;
virtual bool is_currently_false(assignment const& a, bool is_positive) const = 0;
bool is_always_true(bool is_positive) const { return is_always_false(!is_positive); }
bool is_currently_true(solver& s, bool is_positive) const { return is_currently_false(s, !is_positive); }
bool is_currently_true(solver& s, assignment_t const& sub, bool is_positive) const { return is_currently_false(s, sub, !is_positive); }
bool is_currently_true(assignment const& a, bool is_positive) const { return is_currently_false(a, !is_positive); }
virtual void narrow(solver& s, bool is_positive, bool first) = 0;
virtual inequality as_inequality(bool is_positive) const = 0;
@ -155,8 +155,8 @@ namespace polysat {
bool is_always_true() const { return get()->is_always_false(is_negative()); }
bool is_currently_false(solver& s) const { return get()->is_currently_false(s, is_positive()); }
bool is_currently_true(solver& s) const { return get()->is_currently_false(s, is_negative()); }
bool is_currently_false(solver& s, assignment_t const& sub) const { return get()->is_currently_false(s, sub, is_positive()); }
bool is_currently_true(solver& s, assignment_t const& sub) const { return get()->is_currently_false(s, sub, is_negative()); }
bool is_currently_false(assignment const& a) const { return get()->is_currently_false(a, is_positive()); }
bool is_currently_true(assignment const& a) const { return get()->is_currently_false(a, is_negative()); }
lbool bvalue(solver& s) const;
void narrow(solver& s, bool first) { get()->narrow(s, is_positive(), first); }
inequality as_inequality() const { return get()->as_inequality(is_positive()); }

View file

@ -80,12 +80,8 @@ namespace polysat {
return is_always_false(is_positive, p(), q(), r());
}
bool op_constraint::is_currently_false(solver& s, bool is_positive) const {
return is_always_false(is_positive, s.subst(p()), s.subst(q()), s.subst(r()));
}
bool op_constraint::is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const {
return is_always_false(is_positive, s.subst(sub, p()), s.subst(sub, q()), s.subst(sub, r()));
bool op_constraint::is_currently_false(assignment const& a, bool is_positive) const {
return is_always_false(is_positive, a.apply_to(p()), a.apply_to(q()), a.apply_to(r()));
}
std::ostream& op_constraint::display(std::ostream& out, lbool status) const {

View file

@ -60,8 +60,7 @@ namespace polysat {
std::ostream& display(std::ostream& out, lbool status) const override;
std::ostream& display(std::ostream& out) const override;
bool is_always_false(bool is_positive) const override;
bool is_currently_false(solver& s, bool is_positive) const override;
bool is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const override;
bool is_currently_false(assignment const& a, bool is_positive) const override;
void narrow(solver& s, bool is_positive, bool first) override;
inequality as_inequality(bool is_positive) const override { throw default_exception("is not an inequality"); }
unsigned hash() const override;

View file

@ -8,7 +8,7 @@ Module Name:
Author:
Nikolaj Bjorner (nbjorner) 2021-03-19
Jakob Rath 2021-04-6
Jakob Rath 2021-04-06
--*/
@ -89,7 +89,7 @@ namespace polysat {
std::ostream& search_state::display(std::ostream& out) const {
for (auto const& item : m_items)
display(item, out) << " ";
return out;
return out;
}
bool search_state::value(pvar v, rational& val) const {
@ -103,28 +103,7 @@ namespace polysat {
void search_state::push_assignment(pvar p, rational const& r) {
m_items.push_back(search_item::assignment(p));
m_assignment.push_back({p, r});
unsigned sz = s.size(p);
pdd& s = assignment(sz);
m_subst_trail.push_back(s);
s = s.subst_add(p, r);
SASSERT(s == *m_subst[sz]);
}
pdd& search_state::assignment(unsigned sz) const {
m_subst.reserve(sz + 1);
if (!m_subst[sz])
m_subst.set(sz, alloc(pdd, s.sz2pdd(sz).one()));
return *m_subst[sz];
}
void search_state::pop_assignment() {
m_assignment.pop_back();
pdd& s = m_subst_trail.back();
auto& m = s.manager();
unsigned sz = m.power_of_2();
*m_subst[sz] = s;
m_subst_trail.pop_back();
m_assignment.push(p, r);
}
void search_state::push_boolean(sat::literal lit) {
@ -133,8 +112,10 @@ namespace polysat {
void search_state::pop() {
auto const& item = m_items.back();
if (item.is_assignment() && !m_assignment.empty() && item.var() == m_assignment.back().first)
pop_assignment();
if (item.is_assignment()) {
SASSERT_EQ(item.var(), m_assignment.pairs().back().first);
m_assignment.pop();
}
m_items.pop_back();
}

View file

@ -8,20 +8,15 @@ Module Name:
Author:
Nikolaj Bjorner (nbjorner) 2021-03-19
Jakob Rath 2021-04-6
Jakob Rath 2021-04-06
--*/
#pragma once
#include "math/polysat/boolean.h"
#include "math/polysat/types.h"
#include "math/polysat/assignment.h"
namespace polysat {
typedef std::pair<pvar, rational> assignment_item_t;
typedef vector<assignment_item_t> assignment_t;
class solver;
enum class search_item_k
{
assignment,
@ -46,7 +41,7 @@ namespace polysat {
bool is_resolved() const { return m_resolved; }
search_item_k kind() const { return m_kind; }
pvar var() const { SASSERT(is_assignment()); return m_var; }
sat::literal lit() const { SASSERT(is_boolean()); return m_lit; }
sat::literal lit() const { SASSERT(is_boolean()); return m_lit; }
void set_resolved() { m_resolved = true; }
};
@ -54,27 +49,28 @@ namespace polysat {
solver& s;
vector<search_item> m_items;
assignment_t m_assignment; // First-order part of the search state
mutable scoped_ptr_vector<pdd> m_subst;
vector<pdd> m_subst_trail;
assignment m_assignment;
bool value(pvar v, rational& r) const;
public:
search_state(solver& s): s(s) {}
search_state(solver& s): s(s), m_assignment(s) {}
unsigned size() const { return m_items.size(); }
search_item const& back() const { return m_items.back(); }
search_item const& operator[](unsigned i) const { return m_items[i]; }
assignment_t const& assignment() const { return m_assignment; }
pdd& assignment(unsigned sz) const;
substitution const& subst(unsigned sz) const { return m_assignment.subst(sz); }
// TODO: implement the following method if we actually need the assignments without resolved items already during conflict resolution
// (no separate trail needed, just a second m_subst and an index into the trail, I think)
// (update on set_resolved? might be one iteration too early, looking at the old solver::resolve_conflict loop)
substitution const& unresolved_assignment(unsigned sz) const;
void push_assignment(pvar p, rational const& r);
void push_boolean(sat::literal lit);
void pop();
void pop_assignment();
void set_resolved(unsigned i) { m_items[i].set_resolved(); }
using const_iterator = decltype(m_items)::const_iterator;
@ -106,74 +102,4 @@ namespace polysat {
inline std::ostream& operator<<(std::ostream& out, search_item_pp const& p) { return p.verbose ? p.s.display_verbose(p.i, out) : p.s.display(p.i, out); }
// Go backwards over the search_state.
// If new entries are added during processing an item, they will be queued for processing next after the current item.
class search_iterator {
search_state* m_search;
unsigned current;
unsigned first; // highest index + 1
struct idx_range {
unsigned current;
unsigned first; // highest index + 1
};
vector<idx_range> m_index_stack;
void init() {
first = m_search->size();
current = first; // we start one before the beginning
}
void try_push_block() {
if (first != m_search->size()) {
m_index_stack.push_back({current, first});
init();
}
}
void pop_block() {
current = m_index_stack.back().current;
// We don't restore 'first', otherwise 'next()' will immediately push a new block again.
// Instead, the current block is merged with the popped one.
m_index_stack.pop_back();
}
unsigned last() {
return m_index_stack.empty() ? 0 : m_index_stack.back().first;
}
public:
search_iterator(search_state& search):
m_search(&search) {
init();
}
void set_resolved() {
m_search->set_resolved(current);
}
search_item const& operator*() {
return (*m_search)[current];
}
bool next() {
#if 1 // If you want to resolve over constraints that have been added during conflict resolution, enable this.
try_push_block();
#endif
if (current > last()) {
--current;
return true;
}
else {
SASSERT(current == last());
if (m_index_stack.empty())
return false;
pop_block();
return next();
}
}
};
}

View file

@ -133,9 +133,9 @@ namespace polysat {
cl[j++] = cl[i];
else {
DEBUG_CODE({
auto a = s.assignment();
a.push_back({v, k});
SASSERT(s.lit2cnstr(lit).is_currently_false(s, a));
auto a = s.assignment().clone();
a.push(v, k);
SASSERT(s.lit2cnstr(lit).is_currently_false(a));
});
}
}

View file

@ -36,8 +36,7 @@ namespace polysat {
std::ostream& display(std::ostream& out) const override;
bool is_always_false(bool is_positive) const override { return false; }
void narrow(solver& s, bool is_positive, bool first) override;
bool is_currently_false(solver & s, bool is_positive) const override { return false; }
bool is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const override { return false; }
bool is_currently_false(assignment const& a, bool is_positive) const override { return false; }
inequality as_inequality(bool is_positive) const override { throw default_exception("is not an inequality"); }
unsigned hash() const override;

View file

@ -97,7 +97,7 @@ namespace polysat {
return *m_pdd[sz];
}
dd::pdd_manager& solver::var2pdd(pvar v) {
dd::pdd_manager& solver::var2pdd(pvar v) const {
return sz2pdd(size(v));
}
@ -693,15 +693,14 @@ namespace polysat {
SASSERT(is_conflict());
search_iterator search_it(m_search);
while (search_it.next()) {
auto& item = *search_it;
search_it.set_resolved();
for (unsigned i = m_search.size(); i-- > 0; ) {
auto& item = m_search[i];
m_search.set_resolved(i);
if (item.is_assignment()) {
// Resolve over variable assignment
pvar v = item.var();
if (!m_conflict.is_relevant_pvar(v)) {
m_search.pop_assignment();
// m_search.pop_assignment();
continue;
}
LOG_H2("Working on " << search_item_pp(m_search, item));
@ -712,7 +711,7 @@ namespace polysat {
revert_decision(v);
return;
}
m_search.pop_assignment();
// m_search.pop_assignment();
}
else {
// Resolve over boolean literal
@ -834,25 +833,17 @@ namespace polysat {
void solver::backjump_and_learn(unsigned jump_level, clause& lemma) {
#ifndef NDEBUG
assignment_t old_assignment;
// We can't use solver::assignment() here because we already used search_sate::pop_assignment().
// TODO: fix search_state design; it should show a consistent state.
search_iterator search_it(m_search);
while (search_it.next()) {
auto& item = *search_it;
if (item.is_assignment()) {
pvar v = item.var();
old_assignment.push_back({v, get_value(v)});
}
}
polysat::assignment old_assignment = assignment().clone();
sat::literal_vector lemma_invariant_todo;
SASSERT(lemma_invariant_part1(lemma, old_assignment, lemma_invariant_todo));
// SASSERT(lemma_invariant(lemma, old_assignment));
#endif
clause_ref_vector side_lemmas = m_conflict.take_side_lemmas();
sat::literal_vector narrow_queue = m_conflict.take_narrow_queue();
m_conflict.reset();
backjump(jump_level);
for (sat::literal lit : narrow_queue) {
switch (m_bvars.value(lit)) {
case l_true:
@ -899,10 +890,12 @@ namespace polysat {
}
}
bool solver::lemma_invariant_part1(clause const& lemma, assignment_t const& assignment, sat::literal_vector& out_todo) {
bool solver::lemma_invariant_part1(clause const& lemma, polysat::assignment const& a, sat::literal_vector& out_todo) {
SASSERT(out_todo.empty());
LOG("Lemma: " << lemma);
// LOG("assignment: " << assignment);
LOG("assignment: " << a);
// TODO: fix
#if 0
for (sat::literal lit : lemma) {
auto const c = lit2cnstr(lit);
bool const currently_false = c.is_currently_false(*this, assignment);
@ -912,25 +905,42 @@ namespace polysat {
else
SASSERT(m_bvars.value(lit) == l_false || currently_false);
}
#endif
return true;
}
bool solver::lemma_invariant_part2(sat::literal_vector const& todo) {
LOG("todo: " << todo);
// Check that undef literals are now propagated by the side lemmas.
//
// Unfortunately, this isn't always possible.
// Consider if the first side lemma contains a constraint that comes from a boolean decision:
//
// 76: v10 + v7 + -1*v0 + -1 == 0 [ l_true decide@5 pwatched active ]
//
// When we now backtrack behind the decision level of the literal, then we cannot propagate the side lemma,
// and some literals of the main lemma may still be undef at this point.
//
// So it seems that using constraints from a non-asserting lemma makes the new lemma also non-asserting (if it isn't already).
#if 1
for (sat::literal lit : todo)
SASSERT(m_bvars.value(lit) == l_false);
#endif
return true;
}
bool solver::lemma_invariant(clause const& lemma, assignment_t const& old_assignment) {
bool solver::lemma_invariant(clause const& lemma, polysat::assignment const& old_assignment) {
LOG("Lemma: " << lemma);
// LOG("old_assignment: " << old_assignment);
// TODO: fix
#if 0
for (sat::literal lit : lemma) {
auto const c = lit2cnstr(lit);
bool const currently_false = c.is_currently_false(*this, old_assignment);
LOG(" " << lit_pp(*this, lit) << " currently_false? " << currently_false);
SASSERT(m_bvars.value(lit) == l_false || currently_false);
}
#endif
return true;
}
@ -1167,9 +1177,7 @@ namespace polysat {
}
std::ostream& assignments_pp::display(std::ostream& out) const {
for (auto const& [var, val] : s.assignment())
out << assignment_pp(s, var, val) << " ";
return out;
return out << s.assignment();
}
std::ostream& assignment_pp::display(std::ostream& out) const {
@ -1279,18 +1287,7 @@ namespace polysat {
}
pdd solver::subst(pdd const& p) const {
unsigned sz = p.manager().power_of_2();
pdd const& s = m_search.assignment(sz);
return p.subst_val(s);
}
pdd solver::subst(assignment_t const& sub, pdd const& p) const {
unsigned sz = p.manager().power_of_2();
pdd s = p.manager().mk_val(1);
for (auto const& [var, val] : sub)
if (size(var) == sz)
s = p.manager().subst_add(s, var, val);
return p.subst_val(s);
return assignment().apply_to(p);
}
/** Check that boolean assignment and constraint evaluation are consistent */

View file

@ -31,6 +31,7 @@ Author:
#include "math/polysat/justification.h"
#include "math/polysat/linear_solver.h"
#include "math/polysat/search_state.h"
#include "math/polysat/assignment.h"
#include "math/polysat/trail.h"
#include "math/polysat/viable.h"
#include "math/polysat/log.h"
@ -58,6 +59,7 @@ namespace polysat {
stats() { reset(); }
};
friend class assignment;
friend class constraint;
friend class ule_constraint;
friend class umul_ovfl_constraint;
@ -125,8 +127,6 @@ namespace polysat {
unsigned_vector m_size; // store size of variables (bit width)
search_state m_search;
assignment_t const& assignment() const { return m_search.assignment(); }
pdd subst(assignment_t const& sub, pdd const& p) const;
unsigned m_qhead = 0; // next item to propagate (index into m_search)
unsigned m_level = 0;
@ -150,7 +150,6 @@ namespace polysat {
m_qhead_trail.pop_back();
}
unsigned size(pvar v) const { return m_size[v]; }
/**
@ -161,7 +160,9 @@ namespace polysat {
void del_var();
dd::pdd_manager& sz2pdd(unsigned sz) const;
dd::pdd_manager& var2pdd(pvar v);
dd::pdd_manager& var2pdd(pvar v) const;
assignment const& assignment() const { return m_search.assignment(); }
void push_level();
void pop_levels(unsigned num_levels);
@ -235,8 +236,8 @@ namespace polysat {
bool invariant();
static bool invariant(signed_constraints const& cs);
bool lemma_invariant(clause const& lemma, assignment_t const& assignment);
bool lemma_invariant_part1(clause const& lemma, assignment_t const& assignment, sat::literal_vector& out_todo);
bool lemma_invariant(clause const& lemma, polysat::assignment const& a);
bool lemma_invariant_part1(clause const& lemma, polysat::assignment const& a, sat::literal_vector& out_todo);
bool lemma_invariant_part2(sat::literal_vector const& todo);
bool wlist_invariant();
bool assignment_invariant();
@ -429,7 +430,7 @@ namespace polysat {
}; // class solver
class assignments_pp {
class assignments_pp { // TODO: can probably remove this now.
solver const& s;
public:
assignments_pp(solver const& s): s(s) {}

View file

@ -15,8 +15,6 @@ Author:
#include "math/polysat/log.h"
#include "math/polysat/solver.h"
// TODO: rename file
namespace polysat {
struct inference_sup : public inference {
@ -37,8 +35,8 @@ namespace polysat {
SASSERT(c1.is_currently_true(s));
SASSERT(c2.is_currently_false(s));
LOG_H3("Resolving upon v" << v);
LOG("c1: " << c1);
LOG("c2: " << c2);
LOG("c1: " << lit_pp(s, c1));
LOG("c2: " << lit_pp(s, c2));
pdd a = c1.eq();
pdd b = c2.eq();
unsigned degree_a = a.degree();

View file

@ -18,6 +18,7 @@ Author:
#include "util/scoped_ptr_vector.h"
#include "util/var_queue.h"
#include "util/ref_vector.h"
#include "util/sat_literal.h"
#include "math/dd/dd_pdd.h"
#include "math/dd/dd_bdd.h"
#include "math/dd/dd_fdd.h"
@ -25,6 +26,8 @@ Author:
namespace polysat {
class solver;
class clause;
typedef dd::pdd pdd;
typedef dd::bdd bdd;
typedef dd::bddv bddv;

View file

@ -17,7 +17,7 @@ Notes:
Rewrite rules to simplify expressions.
In the following let k, k1, k2 be values.
- k1 <= k2 ==> 0 <= 0 if k1 <= k2
- k1 <= k2 ==> 1 <= 0 if k1 > k2
- 0 <= p ==> 0 <= 0
@ -39,7 +39,7 @@ Notes:
TODO: clause simplifications:
- p + k <= p ==> p + k <= k & p != 0 for k != 0
- p + k <= p ==> p + k <= k & p != 0 for k != 0
- p*q = 0 ==> p = 0 or q = 0 applies to any factoring
- 2*p <= 2*q ==> (p >= 2^n-1 & q < 2^n-1) or (p >= 2^n-1 = q >= 2^n-1 & p <= q)
==> (p >= 2^n-1 => q < 2^n-1 or p <= q) &
@ -50,7 +50,7 @@ TODO: clause simplifications:
Note:
case p <= p + k is already covered because we test (lhs - rhs).is_val()
It can be seen as an instance of lemma 5.2 of Supratik and John.
--*/
@ -168,7 +168,7 @@ namespace polysat {
void ule_constraint::narrow(solver& s, bool is_positive, bool first) {
auto p = s.subst(lhs());
auto q = s.subst(rhs());
signed_constraint sc(this, is_positive);
LOG_H3("Narrowing " << sc);
@ -215,29 +215,23 @@ namespace polysat {
return is_always_false(is_positive, lhs(), rhs());
}
bool ule_constraint::is_currently_false(solver& s, bool is_positive) const {
auto p = s.subst(lhs());
auto q = s.subst(rhs());
return is_always_false(is_positive, p, q);
}
bool ule_constraint::is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const {
auto p = s.subst(sub, lhs());
auto q = s.subst(sub, rhs());
bool ule_constraint::is_currently_false(assignment const& a, bool is_positive) const {
auto p = a.apply_to(lhs());
auto q = a.apply_to(rhs());
return is_always_false(is_positive, p, q);
}
inequality ule_constraint::as_inequality(bool is_positive) const {
if (is_positive)
return inequality(lhs(), rhs(), false, this);
else
else
return inequality(rhs(), lhs(), true, this);
}
unsigned ule_constraint::hash() const {
return mk_mix(lhs().hash(), rhs().hash(), kind());
}
bool ule_constraint::operator==(constraint const& other) const {
return other.is_ule() && lhs() == other.to_ule().lhs() && rhs() == other.to_ule().rhs();
}

View file

@ -37,8 +37,7 @@ namespace polysat {
std::ostream& display(std::ostream& out, lbool status) const override;
std::ostream& display(std::ostream& out) const override;
bool is_always_false(bool is_positive) const override;
bool is_currently_false(solver& s, bool is_positive) const override;
bool is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const override;
bool is_currently_false(assignment const& a, bool is_positive) const override;
void narrow(solver& s, bool is_positive, bool first) override;
inequality as_inequality(bool is_positive) const override;
unsigned hash() const override;

View file

@ -77,8 +77,8 @@ namespace polysat {
return is_always_false(is_positive, m_p, m_q);
}
bool umul_ovfl_constraint::is_currently_false(solver& s, bool is_positive) const {
return is_always_false(is_positive, s.subst(p()), s.subst(q()));
bool umul_ovfl_constraint::is_currently_false(assignment const& a, bool is_positive) const {
return is_always_false(is_positive, a.apply_to(p()), a.apply_to(q()));
}
void umul_ovfl_constraint::narrow(solver& s, bool is_positive, bool first) {

View file

@ -39,8 +39,7 @@ namespace polysat {
std::ostream& display(std::ostream& out) const override;
bool is_always_false(bool is_positive) const override;
void narrow(solver& s, bool is_positive, bool first) override;
bool is_currently_false(solver & s, bool is_positive) const override;
bool is_currently_false(solver& s, assignment_t const& sub, bool is_positive) const override { return false; }
bool is_currently_false(assignment const& a, bool is_positive) const override;
inequality as_inequality(bool is_positive) const override { throw default_exception("is not an inequality"); }
unsigned hash() const override;

View file

@ -60,14 +60,14 @@ namespace polysat {
LOG("lc: " << lc);
LOG("rest: " << rest);
assignment_t a;
pdd const lcs = eval(lc, core, a);
substitution sub(m);
pdd const lcs = eval(lc, core, sub);
LOG("lcs: " << lcs);
pdd lci = m.zero();
if (!inv(lcs, lci))
return;
pdd const rs = s.subst(a, rest);
pdd const rs = sub.apply_to(rest);
pdd const vs = -rs * lci; // this is the polynomial that computes v
LOG("vs: " << vs);
SASSERT(!vs.free_vars().contains(v));
@ -102,7 +102,7 @@ namespace polysat {
continue;
clause_builder cb(s);
for (auto [w, wv] : a)
for (auto [w, wv] : sub)
cb.insert(~s.eq(s.var(w), wv));
cb.insert(~c);
cb.insert(~c_target);
@ -112,19 +112,19 @@ namespace polysat {
}
// Evaluate p under assignments in the core.
pdd free_variable_elimination::eval(pdd const& p, conflict& core, assignment_t& out_assignment) {
pdd free_variable_elimination::eval(pdd const& p, conflict& core, substitution& out_sub) {
// TODO: this should probably be a helper method on conflict.
// TODO: recognize constraints of the form "v1 == 27" to be used in the assignment?
// (but maybe useful evaluations are always part of core.vars() anyway?)
assignment_t& a = out_assignment;
SASSERT(a.empty());
substitution& sub = out_sub;
SASSERT(sub.empty());
for (auto v : p.free_vars())
if (core.contains_pvar(v))
a.push_back({v, s.get_value(v)});
sub.add(v, s.get_value(v));
pdd q = s.subst(a, p);
pdd q = sub.apply_to(p);
// TODO: like in the old conflict::minimize_vars, we can now try to remove unnecessary variables from a.

View file

@ -24,7 +24,7 @@ namespace polysat {
solver& s;
void find_lemma(pvar v, conflict& core);
void find_lemma(pvar v, signed_constraint c, conflict& core);
pdd eval(pdd const& p, conflict& core, assignment_t& out_assignment);
pdd eval(pdd const& p, conflict& core, substitution& out_sub);
bool inv(pdd const& p, pdd& out_p_inv);
public:
free_variable_elimination(solver& s): s(s) {}