3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-01 08:53:18 +00:00

Merge branch 'master' of https://github.com/z3prover/z3 into polysat

This commit is contained in:
Nikolaj Bjorner 2021-08-30 10:00:58 -07:00
commit 39f50d46cc
82 changed files with 1049 additions and 599 deletions

View file

@ -67,4 +67,7 @@ inline std::string operator+(std::string const& s, mk_pp const& pp) {
return strm.str();
}
inline std::string& operator+=(std::string& s, mk_pp const& pp) {
return s = s + pp;
}

View file

@ -367,6 +367,25 @@ namespace datatype {
return m.mk_func_decl(name, arity, domain, range, info);
}
ptr_vector<constructor> plugin::get_constructors(symbol const& s) const {
ptr_vector<constructor> result;
for (auto [k, d] : m_defs)
for (auto* c : *d)
if (c->name() == s)
result.push_back(c);
return result;
}
ptr_vector<accessor> plugin::get_accessors(symbol const& s) const {
ptr_vector<accessor> result;
for (auto [k, d] : m_defs)
for (auto* c : *d)
for (auto* a : *c)
if (a->name() == s)
result.push_back(a);
return result;
}
func_decl * decl::plugin::mk_recognizer(unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort *) {
ast_manager& m = *m_manager;
@ -556,9 +575,8 @@ namespace datatype {
void plugin::remove(symbol const& s) {
def* d = nullptr;
if (m_defs.find(s, d)) {
if (m_defs.find(s, d))
dealloc(d);
}
m_defs.remove(s);
}
@ -688,18 +706,18 @@ namespace datatype {
\brief Return true if the inductive datatype is recursive.
*/
bool util::is_recursive_core(sort* s) const {
obj_map<sort, status> already_found;
map<symbol, status, symbol_hash_proc, symbol_eq_proc> already_found;
ptr_vector<sort> todo, subsorts;
sort* s0 = s;
todo.push_back(s);
status st;
status st;
while (!todo.empty()) {
s = todo.back();
if (already_found.find(s, st) && st == BLACK) {
if (already_found.find(datatype_name(s), st) && st == BLACK) {
todo.pop_back();
continue;
}
already_found.insert(s, GRAY);
already_found.insert(datatype_name(s), GRAY);
def const& d = get_def(s);
bool can_process = true;
for (constructor const* c : d) {
@ -710,9 +728,9 @@ namespace datatype {
get_subsorts(d, subsorts);
for (sort * s2 : subsorts) {
if (is_datatype(s2)) {
if (already_found.find(s2, st)) {
if (already_found.find(datatype_name(s2), st)) {
// type is recursive
if (st == GRAY && s0 == s2)
if (st == GRAY && datatype_name(s0) == datatype_name(s2))
return true;
}
else {
@ -724,7 +742,7 @@ namespace datatype {
}
}
if (can_process) {
already_found.insert(s, BLACK);
already_found.insert(datatype_name(s), BLACK);
todo.pop_back();
}
}

View file

@ -248,6 +248,8 @@ namespace datatype {
def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); }
def& get_def(symbol const& s) { return *(m_defs[s]); }
ptr_vector<constructor> get_constructors(symbol const& s) const;
ptr_vector<accessor> get_accessors(symbol const& s) const;
bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); }
unsigned get_axiom_base_id(symbol const& s) { return m_axiom_bases[s]; }
util & u() const;
@ -321,6 +323,7 @@ namespace datatype {
bool is_covariant(ast_mark& mark, ptr_vector<sort>& subsorts, sort* s) const;
def& get_def(symbol const& s) { return plugin().get_def(s); }
void get_subsorts(sort* s, ptr_vector<sort>& sorts) const;
symbol datatype_name(sort* s) const { return s->get_parameter(0).get_symbol(); }
public:
util(ast_manager & m);

View file

@ -99,7 +99,7 @@ namespace euf {
void egraph::update_children(enode* n) {
for (enode* child : enode_args(n))
child->get_root()->add_parent(n);
n->set_update_children();
m_updates.push_back(update_record(n, update_record::update_children()));
}
enode* egraph::mk(expr* f, unsigned generation, unsigned num_args, enode *const* args) {
@ -118,14 +118,14 @@ namespace euf {
n->set_is_equality();
update_children(n);
reinsert_equality(n);
return n;
}
enode_bool_pair p = insert_table(n);
enode* n2 = p.first;
if (n2 == n)
update_children(n);
else
merge(n, n2, justification::congruence(p.second));
else {
auto [n2, comm] = insert_table(n);
if (n2 == n)
update_children(n);
else
merge(n, n2, justification::congruence(comm));
}
return n;
}
@ -264,18 +264,21 @@ namespace euf {
void egraph::set_merge_enabled(enode* n, bool enable_merge) {
if (enable_merge != n->merge_enabled()) {
toggle_merge_enabled(n);
toggle_merge_enabled(n, false);
m_updates.push_back(update_record(n, update_record::toggle_merge()));
}
}
void egraph::toggle_merge_enabled(enode* n) {
void egraph::toggle_merge_enabled(enode* n, bool backtracking) {
bool enable_merge = !n->merge_enabled();
n->set_merge_enabled(enable_merge);
if (n->num_args() > 0) {
if (enable_merge)
insert_table(n);
else if (m_table.contains_ptr(n))
if (enable_merge) {
auto [n2, comm] = insert_table(n);
if (n2 != n && !backtracking)
m_to_merge.push_back(to_merge(n, n2, comm));
}
else if (n->is_cgr())
erase_from_table(n);
}
VERIFY(n->num_args() == 0 || !n->merge_enabled() || m_table.contains(n));
@ -332,14 +335,15 @@ namespace euf {
m_nodes.pop_back();
m_exprs.pop_back();
};
for (unsigned i = m_updates.size(); i-- > num_updates; ) {
unsigned sz = m_updates.size();
for (unsigned i = sz; i-- > num_updates; ) {
auto const& p = m_updates[i];
switch (p.tag) {
case update_record::tag_t::is_add_node:
undo_node();
break;
case update_record::tag_t::is_toggle_merge:
toggle_merge_enabled(p.r1);
toggle_merge_enabled(p.r1, true);
break;
case update_record::tag_t::is_set_parent:
undo_eq(p.r1, p.n1, p.r2_num_parents);
@ -376,12 +380,18 @@ namespace euf {
case update_record::tag_t::is_lbl_set:
p.r1->m_lbls.set(p.m_lbls);
break;
case update_record::tag_t::is_update_children:
for (unsigned i = 0; i < p.r1->num_args(); ++i) {
SASSERT(p.r1->m_args[i]->get_root()->m_parents.back() == p.r1);
p.r1->m_args[i]->get_root()->m_parents.pop_back();
}
break;
default:
UNREACHABLE();
break;
}
}
}
SASSERT(m_updates.size() == sz);
m_updates.shrink(num_updates);
m_scopes.shrink(old_lim);
m_region.pop_scope(num_scopes);
@ -403,7 +413,7 @@ namespace euf {
if (r1 == r2)
return;
TRACE("euf", j.display(tout << "merge: " << bpp(n1) << " == " << bpp(n2) << " ", m_display_justification) << "\n";);
TRACE("euf", j.display(tout << "merge: " << bpp(n1) << " == " << bpp(n2) << " ", m_display_justification) << "\n" << bpp(r1) << " " << bpp(r2) << "\n";);
IF_VERBOSE(20, j.display(verbose_stream() << "merge: " << bpp(n1) << " == " << bpp(n2) << " ", m_display_justification) << "\n";);
force_push();
SASSERT(m_num_scopes == 0);
@ -457,12 +467,13 @@ namespace euf {
if (!p->is_marked1())
continue;
p->unmark1();
TRACE("euf", tout << "reinsert " << bpp(r1) << " " << bpp(r2) << " " << bpp(p) << " " << p->merge_enabled() << "\n";);
if (p->merge_enabled()) {
auto rc = insert_table(p);
enode* p_other = rc.first;
auto [p_other, comm] = insert_table(p);
SASSERT(m_table.contains_ptr(p) == (p_other == p));
TRACE("euf", tout << "other " << bpp(p_other) << "\n";);
if (p_other != p)
m_to_merge.push_back(to_merge(p_other, p, rc.second));
m_to_merge.push_back(to_merge(p_other, p, comm));
else
r2->m_parents.push_back(p);
if (p->is_equality())
@ -752,7 +763,7 @@ namespace euf {
out << "] ";
}
if (n->value() != l_undef)
out << "[b" << n->bool_var() << " := " << (n->value() == l_true ? "T":"F") << "] ";
out << "[b" << n->bool_var() << " := " << (n->value() == l_true ? "T":"F") << (n->merge_tf()?"":" no merge") << "] ";
if (n->has_th_vars()) {
out << "[t";
for (auto v : enode_th_vars(n))

View file

@ -104,7 +104,8 @@ namespace euf {
struct value_assignment {};
struct lbl_hash {};
struct lbl_set {};
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge,
struct update_children {};
enum class tag_t { is_set_parent, is_add_node, is_toggle_merge, is_update_children,
is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq,
is_lbl_hash, is_new_th_eq_qhead, is_new_lits_qhead,
is_inconsistent, is_value_assignment, is_lbl_set };
@ -148,6 +149,8 @@ namespace euf {
tag(tag_t::is_lbl_hash), r1(n), n1(nullptr), m_lbl_hash(n->m_lbl_hash) {}
update_record(enode* n, lbl_set):
tag(tag_t::is_lbl_set), r1(n), n1(nullptr), m_lbls(n->m_lbls.get()) {}
update_record(enode* n, update_children) :
tag(tag_t::is_update_children), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {}
};
ast_manager& m;
svector<to_merge> m_to_merge;
@ -211,7 +214,7 @@ namespace euf {
void push_to_lca(enode* a, enode* lca);
void push_congruence(enode* n1, enode* n2, bool commutative);
void push_todo(enode* n);
void toggle_merge_enabled(enode* n);
void toggle_merge_enabled(enode* n, bool backtracking);
enode_bool_pair insert_table(enode* p);
void erase_from_table(enode* p);
@ -235,7 +238,7 @@ namespace euf {
enode* find(expr* f, unsigned n, enode* const* args);
enode* mk(expr* f, unsigned generation, unsigned n, enode *const* args);
enode_vector const& enodes_of(func_decl* f);
void push() { ++m_num_scopes; }
void push() { if (!m_to_merge.empty()) propagate(); ++m_num_scopes; }
void pop(unsigned num_scopes);
/**

View file

@ -46,7 +46,6 @@ namespace euf {
bool m_mark1 = false;
bool m_mark2 = false;
bool m_commutative = false;
bool m_update_children = false;
bool m_interpreted = false;
bool m_merge_enabled = true;
bool m_is_equality = false; // Does the expression represent an equality
@ -124,10 +123,7 @@ namespace euf {
n->m_args[i] = nullptr;
return n;
}
void set_update_children() { m_update_children = true; }
friend class add_th_var_trail;
friend class replace_th_var_trail;
void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); }
@ -142,12 +138,6 @@ namespace euf {
~enode() {
SASSERT(m_root == this);
SASSERT(class_size() == 1);
if (m_update_children) {
for (unsigned i = 0; i < num_args(); ++i) {
SASSERT(m_args[i]->get_root()->m_parents.back() == this);
m_args[i]->get_root()->m_parents.pop_back();
}
}
}
enode* const* args() const { return m_args; }

View file

@ -19,6 +19,7 @@ Notes:
#include<math.h>
#include "ast/ast_smt2_pp.h"
#include "ast/ast_pp.h"
#include "ast/well_sorted.h"
#include "ast/rewriter/th_rewriter.h"
#include "ast/used_vars.h"
@ -4430,7 +4431,7 @@ expr* fpa2bv_converter_wrapped::bv2fpa_value(sort* s, expr* a, expr* b, expr* c)
mpfm.set(f, ebits, sbits, mpzm.is_one(sgn_z), mpzm.get_int64(exp_u), sig_z);
result = m_util.mk_value(f);
TRACE("t_fpa", tout << "result: [" <<
TRACE("t_fpa", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << " result: [" <<
mpzm.to_string(sgn_z) << "," <<
mpzm.to_string(exp_z) << "," <<
mpzm.to_string(sig_z) << "] --> " <<

View file

@ -126,7 +126,7 @@ bool macro_manager::insert(func_decl * f, quantifier * q, proof * pr, expr_depen
}
app * head;
expr * definition;
expr_ref definition(m);
bool revert = false;
get_head_def(q, f, head, definition, revert);
@ -190,21 +190,23 @@ void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) {
}
void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def, bool& revert) const {
app * body = to_app(q->get_expr());
void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr_ref & def, bool& revert) const {
expr * body = q->get_expr();
expr * lhs = nullptr, *rhs = nullptr;
bool is_not = m.is_not(body, body);
VERIFY(m.is_eq(body, lhs, rhs));
SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d));
SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d));
SASSERT(!is_not || m.is_bool(lhs));
if (is_app_of(lhs, d)) {
revert = false;
head = to_app(lhs);
def = rhs;
def = is_not ? m.mk_not(rhs) : rhs;
}
else {
revert = true;
head = to_app(rhs);
def = lhs;
def = is_not ? m.mk_not(lhs) : lhs;
}
}
@ -215,7 +217,7 @@ void macro_manager::display(std::ostream & out) {
quantifier * q = nullptr;
m_decl2macro.find(f, q);
app * head;
expr * def;
expr_ref def(m);
bool r;
get_head_def(q, f, head, def, r);
SASSERT(q);
@ -227,7 +229,7 @@ func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & inter
func_decl * f = m_decls.get(i);
quantifier * q = m_macros.get(i);
app * head;
expr * def;
expr_ref def(m);
bool r;
get_head_def(q, f, head, def, r);
TRACE("macro_bug",
@ -298,7 +300,7 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg {
TRACE("macro_manager", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";);
if (mm.m_decl2macro.find(d, q)) {
app * head = nullptr;
expr * def = nullptr;
expr_ref def(m);
bool revert = false;
mm.get_head_def(q, d, head, def, revert);
unsigned num = n->get_num_args();
@ -320,6 +322,14 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg {
r = rr;
if (m.proofs_enabled()) {
expr_ref instance = s(q->get_expr(), num, subst_args.data());
expr* eq, * lhs, * rhs;
if (m.is_not(instance, eq) && m.is_eq(eq, lhs, rhs)) {
if (revert)
instance = m.mk_eq(m.mk_not(lhs), rhs);
else
instance = m.mk_eq(lhs, m.mk_not(rhs));
}
SASSERT(m.is_eq(instance));
proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.data());
proof * q_pr = mm.m_decl2macro_pr.find(d);
proof * prs[2] = { qi_pr, q_pr };

View file

@ -84,7 +84,7 @@ public:
func_decl * get_macro_func_decl(unsigned i) const { return m_decls.get(i); }
func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const;
quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = nullptr; m_decl2macro.find(f, q); return q; }
void get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def, bool& revert) const;
void get_head_def(quantifier * q, func_decl * d, app * & head, expr_ref & def, bool& revert) const;
void expand_macros(expr * n, proof * pr, expr_dependency * dep, expr_ref & r, proof_ref & new_pr, expr_dependency_ref & new_dep);

View file

@ -184,6 +184,15 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he
def = rhs;
return true;
}
if (m_manager.is_not(n, lhs) && m_manager.is_eq(lhs, lhs, rhs) &&
m_manager.is_bool(lhs) &&
is_macro_head(lhs, num_decls) &&
!is_forbidden(to_app(lhs)->get_decl()) &&
!occurs(to_app(lhs)->get_decl(), rhs)) {
head = to_app(lhs);
def = m_manager.mk_not(rhs);
return true;
}
return false;
}
@ -215,6 +224,15 @@ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & h
def = lhs;
return true;
}
if (m_manager.is_not(n, n) && m_manager.is_eq(n, lhs, rhs) &&
m_manager.is_bool(lhs) &&
is_macro_head(rhs, num_decls) &&
!is_forbidden(to_app(rhs)->get_decl()) &&
!occurs(to_app(rhs)->get_decl(), lhs)) {
head = to_app(rhs);
def = m_manager.mk_not(lhs);
return true;
}
return false;
}

View file

@ -148,6 +148,15 @@ bool quasi_macros::depends_on(expr * e, func_decl * f) const {
return false;
}
bool quasi_macros::is_quasi_def(quantifier* q, expr* lhs, expr* rhs) const {
return
is_non_ground_uninterp(lhs) &&
is_unique(to_app(lhs)->get_decl()) &&
!depends_on(rhs, to_app(lhs)->get_decl()) &&
fully_depends_on(to_app(lhs), q);
}
bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
// Our definition of a quasi-macro:
// Forall X. f[X] = T[X], where f[X] is a term starting with symbol f, f is uninterpreted,
@ -158,27 +167,39 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
quantifier * q = to_quantifier(e);
expr * qe = q->get_expr(), *lhs = nullptr, *rhs = nullptr;
if (m.is_eq(qe, lhs, rhs)) {
if (is_non_ground_uninterp(lhs) && is_unique(to_app(lhs)->get_decl()) &&
!depends_on(rhs, to_app(lhs)->get_decl()) && fully_depends_on(to_app(lhs), q)) {
a = to_app(lhs);
if (is_quasi_def(q, lhs, rhs)) {
a = to_app(lhs);
t = rhs;
return true;
} else if (is_non_ground_uninterp(rhs) && is_unique(to_app(rhs)->get_decl()) &&
!depends_on(lhs, to_app(rhs)->get_decl()) && fully_depends_on(to_app(rhs), q)) {
a = to_app(rhs);
} else if (is_quasi_def(q, rhs, lhs)) {
a = to_app(rhs);
t = lhs;
return true;
}
} else if (m.is_not(qe, lhs) && is_non_ground_uninterp(lhs) &&
}
else if (m.is_not(qe, lhs) && is_non_ground_uninterp(lhs) &&
is_unique(to_app(lhs)->get_decl())) { // this is like f(...) = false
a = to_app(lhs);
t = m.mk_false();
return true;
} else if (is_non_ground_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true
}
else if (is_non_ground_uninterp(qe) && is_unique(to_app(qe)->get_decl())) { // this is like f(...) = true
a = to_app(qe);
t = m.mk_true();
return true;
}
else if (m.is_not(qe, lhs) && m.is_eq(lhs, lhs, rhs) && m.is_bool(lhs)) {
if (is_quasi_def(q, lhs, rhs)) {
a = to_app(lhs);
t = m.mk_not(rhs);
return true;
} else if (is_quasi_def(q, rhs, lhs)) {
a = to_app(rhs);
t = m.mk_not(lhs);
return true;
}
}
}
return false;

View file

@ -49,6 +49,7 @@ class quasi_macros {
bool depends_on(expr * e, func_decl * f) const;
bool is_quasi_macro(expr * e, app_ref & a, expr_ref &v) const;
bool is_quasi_def(quantifier* q, expr* lhs, expr* rhs) const;
bool quasi_macro_to_macro(quantifier * q, app * a, expr * t, quantifier_ref & macro);
void find_occurrences(expr * e);

View file

@ -695,6 +695,12 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
if (m_util.is_const(rhs) && m_util.is_store(lhs)) {
std::swap(lhs, rhs);
}
if (m_util.is_const(lhs, v) && m_util.is_store(rhs)) {
unsigned n = to_app(rhs)->get_num_args();
result = m().mk_and(m().mk_eq(lhs, to_app(rhs)->get_arg(0)),
m().mk_eq(v, to_app(rhs)->get_arg(n - 1)));
return BR_REWRITE2;
}
if (m_util.is_const(lhs, v) && m_util.is_const(rhs, w)) {
result = m().mk_eq(v, w);
return BR_REWRITE1;

View file

@ -35,16 +35,12 @@ protected:
void mk_ext_rotate_left_right(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
unsigned long long m_max_memory;
bool m_use_wtm; /* Wallace Tree Multiplier */
bool m_use_bcm; /* Booth Multiplier for constants */
void checkpoint();
public:
bit_blaster_tpl(Cfg const & cfg = Cfg(), unsigned long long max_memory = UINT64_MAX, bool use_wtm = false, bool use_bcm=false):
bit_blaster_tpl(Cfg const & cfg = Cfg(), unsigned long long max_memory = UINT64_MAX):
Cfg(cfg),
m_max_memory(max_memory),
m_use_wtm(use_wtm),
m_use_bcm(use_bcm) {
m_max_memory(max_memory) {
}
void set_max_memory(unsigned long long max_memory) {
@ -121,7 +117,6 @@ public:
void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits);
bool mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
bool mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits);
void mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer<expr, 128>& a_bits, ptr_buffer<expr, 128>& b_bits, expr_ref_vector & out_bits);

View file

@ -195,168 +195,88 @@ void bit_blaster_tpl<Cfg>::mk_multiplier(unsigned sz, expr * const * a_bits, exp
return;
}
if (mk_const_multiplier(sz, a_bits, b_bits, out_bits)) {
SASSERT(sz == out_bits.size());
return;
}
if (mk_const_multiplier(sz, b_bits, a_bits, out_bits)) {
if (mk_const_case_multiplier(sz, a_bits, b_bits, out_bits)) {
SASSERT(sz == out_bits.size());
return;
}
out_bits.reset();
if (!m_use_wtm) {
#if 0
static unsigned counter = 0;
counter++;
verbose_stream() << "MK_MULTIPLIER: " << counter << std::endl;
#endif
expr_ref_vector cins(m()), couts(m());
expr_ref out(m()), cout(m());
mk_and(a_bits[0], b_bits[0], out);
out_bits.push_back(out);
expr_ref_vector cins(m()), couts(m());
expr_ref out(m()), cout(m());
/*
out = a*b is encoded using the following circuit.
a[0]&b[0] a[0]&b[1] a[0]&b[2] a[0]&b[3] ...
| | | |
| a[1]&b[0] - HA a[1]&b[1] - HA a[1]&b[2] - HA
| | \ | \ | \
| | --------------- | -------------- | --- ...
| | \| \
| | a[2]&b[0] - FA a[2]&b[1] - FA
| | | \ | \
| | | -------------- | -- ...
| | | \|
| | | a[3]&b[0] - FA
| | | | \
| | | | -- ....
... ... ... ...
out[0] out[1] out[2] out[3]
HA denotes a half-adder.
FA denotes a full-adder.
*/
mk_and(a_bits[0], b_bits[0], out);
out_bits.push_back(out);
for (unsigned i = 1; i < sz; i++) {
checkpoint();
couts.reset();
expr_ref i1(m()), i2(m());
mk_and(a_bits[0], b_bits[i], i1);
mk_and(a_bits[1], b_bits[i-1], i2);
if (i < sz - 1) {
mk_half_adder(i1, i2, out, cout);
/*
out = a*b is encoded using the following circuit.
a[0]&b[0] a[0]&b[1] a[0]&b[2] a[0]&b[3] ...
| | | |
| a[1]&b[0] - HA a[1]&b[1] - HA a[1]&b[2] - HA
| | \ | \ | \
| | --------------- | -------------- | --- ...
| | \| \
| | a[2]&b[0] - FA a[2]&b[1] - FA
| | | \ | \
| | | -------------- | -- ...
| | | \|
| | | a[3]&b[0] - FA
| | | | \
| | | | -- ....
... ... ... ...
out[0] out[1] out[2] out[3]
HA denotes a half-adder.
FA denotes a full-adder.
*/
for (unsigned i = 1; i < sz; i++) {
checkpoint();
couts.reset();
expr_ref i1(m()), i2(m());
mk_and(a_bits[0], b_bits[i], i1);
mk_and(a_bits[1], b_bits[i - 1], i2);
if (i < sz - 1) {
mk_half_adder(i1, i2, out, cout);
couts.push_back(cout);
for (unsigned j = 2; j <= i; j++) {
expr_ref prev_out(m());
prev_out = out;
expr_ref i3(m());
mk_and(a_bits[j], b_bits[i - j], i3);
mk_full_adder(i3, prev_out, cins.get(j - 2), out, cout);
couts.push_back(cout);
for (unsigned j = 2; j <= i; j++) {
expr_ref prev_out(m());
prev_out = out;
expr_ref i3(m());
mk_and(a_bits[j], b_bits[i-j], i3);
mk_full_adder(i3, prev_out, cins.get(j-2), out, cout);
couts.push_back(cout);
}
out_bits.push_back(out);
cins.swap(couts);
}
else {
// last step --> I don't need to generate/store couts.
mk_xor(i1, i2, out);
for (unsigned j = 2; j <= i; j++) {
expr_ref i3(m());
mk_and(a_bits[j], b_bits[i-j], i3);
mk_xor3(i3, out, cins.get(j-2), out);
}
out_bits.push_back(out);
out_bits.push_back(out);
cins.swap(couts);
}
else {
// last step --> I don't need to generate/store couts.
mk_xor(i1, i2, out);
for (unsigned j = 2; j <= i; j++) {
expr_ref i3(m());
mk_and(a_bits[j], b_bits[i - j], i3);
mk_xor3(i3, out, cins.get(j - 2), out);
}
out_bits.push_back(out);
}
}
else {
// WALLACE TREE MULTIPLIER
if (sz == 1) {
expr_ref t(m());
mk_and(a_bits[0], b_bits[0], t);
out_bits.push_back(t);
return;
}
// There are sz numbers to add and we use a Wallace tree to reduce that to two.
// In this tree, we reduce as early as possible, as opposed to the Dada tree where some
// additions may be delayed if they don't increase the propagation delay [which may be
// a little bit more efficient, but it's tricky to find out which additions create
// additional delays].
expr_ref zero(m());
zero = m().mk_false();
vector< expr_ref_vector > pps;
pps.resize(sz, expr_ref_vector(m()));
for (unsigned i = 0; i < sz; i++) {
checkpoint();
// The partial product is a_bits AND b_bits[i]
// [or alternatively ITE(b_bits[i], a_bits, bv0[sz])]
expr_ref_vector & pp = pps[i];
expr_ref t(m());
for (unsigned j = 0; j < i; j++)
pp.push_back(zero); // left shift by i bits
for (unsigned j = 0; j < (sz - i); j++) {
mk_and(a_bits[j], b_bits[i], t);
pp.push_back(t);
}
SASSERT(pps[i].size() == sz);
}
while (pps.size() != 2) {
unsigned save_inx = 0;
unsigned i = 0;
unsigned end = pps.size() - 3;
for ( ; i <= end; i += 3) {
checkpoint();
expr_ref_vector pp1(m()), pp2(m()), pp3(m());
pp1.swap(pps[i]);
pp2.swap(pps[i+1]);
pp3.swap(pps[i+2]);
expr_ref_vector & sum_bits = pps[save_inx];
expr_ref_vector & carry_bits = pps[save_inx+1];
SASSERT(sum_bits.empty() && carry_bits.empty());
carry_bits.push_back(zero);
mk_carry_save_adder(pp1.size(), pp1.data(), pp2.data(), pp3.data(), sum_bits, carry_bits);
carry_bits.pop_back();
save_inx += 2;
}
if (i == pps.size()-2) {
pps[save_inx++].swap(pps[i++]);
pps[save_inx++].swap(pps[i++]);
}
else if (i == pps.size()-1) {
pps[save_inx++].swap(pps[i++]);
}
SASSERT (save_inx < pps.size() && i == pps.size());
pps.shrink(save_inx);
}
SASSERT(pps.size() == 2);
// Now there are only two numbers to add, we can use a ripple carry adder here.
mk_adder(sz, pps[0].data(), pps[1].data(), out_bits);
}
}
template<typename Cfg>
void bit_blaster_tpl<Cfg>::mk_umul_no_overflow(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref & result) {
void bit_blaster_tpl<Cfg>::mk_umul_no_overflow(unsigned sz, expr* const* a_bits, expr* const* b_bits, expr_ref& result) {
SASSERT(sz > 0);
expr_ref zero(m());
zero = m().mk_false();
ptr_buffer<expr,128> ext_a_bits;
ptr_buffer<expr,128> ext_b_bits;
ptr_buffer<expr, 128> ext_a_bits;
ptr_buffer<expr, 128> ext_b_bits;
ext_a_bits.append(sz, a_bits);
ext_b_bits.append(sz, b_bits);
ext_a_bits.push_back(zero);
@ -1196,30 +1116,32 @@ bool bit_blaster_tpl<Cfg>::mk_const_case_multiplier(unsigned sz, expr * const *
unsigned case_size = 1;
unsigned circuit_size = sz*sz*5;
for (unsigned i = 0; case_size < circuit_size && i < sz; ++i) {
if (!is_bool_const(a_bits[i])) {
case_size *= 2;
}
if (!is_bool_const(b_bits[i])) {
case_size *= 2;
}
if (!is_bool_const(a_bits[i]))
case_size *= 2;
if (!is_bool_const(b_bits[i]))
case_size *= 2;
}
if (case_size >= circuit_size) {
if (case_size >= circuit_size)
return false;
}
SASSERT(out_bits.empty());
ptr_buffer<expr, 128> na_bits;
na_bits.append(sz, a_bits);
ptr_buffer<expr, 128> nb_bits;
nb_bits.append(sz, b_bits);
mk_const_case_multiplier(true, 0, sz, na_bits, nb_bits, out_bits);
return false;
return true;
}
template<typename Cfg>
void bit_blaster_tpl<Cfg>::mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer<expr, 128>& a_bits, ptr_buffer<expr, 128>& b_bits, expr_ref_vector & out_bits) {
while (is_a && i < sz && is_bool_const(a_bits[i])) ++i;
if (is_a && i == sz) { is_a = false; i = 0; }
while (!is_a && i < sz && is_bool_const(b_bits[i])) ++i;
if (is_a && i == sz) {
is_a = false;
i = 0;
}
while (!is_a && i < sz && is_bool_const(b_bits[i]))
++i;
if (i < sz) {
expr_ref_vector out1(m()), out2(m());
expr_ref x(m());
@ -1230,9 +1152,11 @@ void bit_blaster_tpl<Cfg>::mk_const_case_multiplier(bool is_a, unsigned i, unsig
mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out2);
if (is_a) a_bits[i] = x; else b_bits[i] = x;
SASSERT(out_bits.empty());
expr_ref bit(m());
for (unsigned j = 0; j < sz; ++j) {
out_bits.push_back(m().mk_ite(x, out1[j].get(), out2[j].get()));
}
mk_ite(x, out1.get(j), out2.get(j), bit);
out_bits.push_back(bit);
}
}
else {
numeral n_a, n_b;
@ -1244,101 +1168,3 @@ void bit_blaster_tpl<Cfg>::mk_const_case_multiplier(bool is_a, unsigned i, unsig
}
SASSERT(out_bits.size() == sz);
}
template<typename Cfg>
bool bit_blaster_tpl<Cfg>::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) {
numeral n_a;
if (!is_numeral(sz, a_bits, n_a)) {
return false;
}
SASSERT(out_bits.empty());
if (mk_const_case_multiplier(sz, a_bits, b_bits, out_bits)) {
SASSERT(sz == out_bits.size());
return true;
}
out_bits.reset();
if (!m_use_bcm) {
return false;
}
expr_ref_vector minus_b_bits(m()), tmp(m());
mk_neg(sz, b_bits, minus_b_bits);
out_bits.resize(sz, m().mk_false());
#if 1
bool last = false, now;
for (unsigned i = 0; i < sz; i++) {
now = m().is_true(a_bits[i]);
SASSERT(now || m().is_false(a_bits[i]));
tmp.reset();
if (now && !last) {
mk_adder(sz - i, out_bits.data() + i, minus_b_bits.data(), tmp);
for (unsigned j = 0; j < (sz - i); j++)
out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux.
}
else if (!now && last) {
mk_adder(sz - i, out_bits.data() + i, b_bits, tmp);
for (unsigned j = 0; j < (sz - i); j++)
out_bits.set(i+j, tmp.get(j)); // do not use [], it does not work on Linux.
}
last = now;
}
#else
// Radix 4 Booth encoder
// B = b_bits, -B = minus_b_bits
// 2B = b2_bits, -2B = minus_b2_bits
expr_ref_vector b2_bits(m());
expr_ref_vector minus_b2_bits(m());
b2_bits.push_back(m().mk_false());
minus_b2_bits.push_back(m().mk_false());
for (unsigned i = 0; i < sz-1; i++) {
b2_bits.push_back(b_bits[i]);
minus_b2_bits.push_back(minus_b_bits.get(i));
}
bool last=false, now1, now2;
for (unsigned i = 0; i < sz; i += 2) {
now1 = m().is_true(a_bits[i]);
now2 = m().is_true(a_bits[i+1]);
SASSERT(now1 || m().is_false(a_bits[i]));
SASSERT(now2 || m().is_false(a_bits[i+1]));
tmp.reset();
if ((!now2 && !now1 && last) ||
(!now2 && now1 && !last)) { // Add B
mk_adder(sz - i, out_bits.c_ptr() + i, b_bits, tmp);
for (unsigned j = 0; j < (sz - i); j++)
out_bits.set(i+j, tmp.get(j));
}
else if (!now2 && now1 && last) { // Add 2B
mk_adder(sz - i, out_bits.c_ptr() + i, b2_bits.c_ptr(), tmp);
for (unsigned j = 0; j < (sz - i); j++)
out_bits.set(i+j, tmp.get(j));
}
else if (now2 && !now1 && !last) { // Add -2B
mk_adder(sz - i, out_bits.c_ptr() + i, minus_b2_bits.c_ptr(), tmp);
for (unsigned j = 0; j < (sz - i); j++)
out_bits.set(i+j, tmp.get(j));
}
else if ((now2 && !now1 && last) ||
(now2 && now1 && !last)) { // Add -B
mk_adder(sz - i, out_bits.c_ptr() + i, minus_b_bits.c_ptr(), tmp);
for (unsigned j = 0; j < (sz - i); j++)
out_bits.set(i+j, tmp.get(j));
}
last = now2;
}
#endif
TRACE("bit_blaster_tpl_booth", for (unsigned i=0; i<out_bits.size(); i++)
tout << "Booth encoding: " << mk_pp(out_bits[i].get(), m()) << "\n"; );
SASSERT(out_bits.size() == sz);
return true;
}

View file

@ -721,9 +721,18 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
result = m().mk_false();
return BR_DONE;
}
if (m().is_not(rhs))
std::swap(lhs, rhs);
if (m().is_not(lhs, lhs)) {
result = m().mk_not(m().mk_eq(lhs, rhs));
return BR_REWRITE2;
}
if (unfolded) {
result = mk_eq(lhs, rhs);
return BR_DONE;
return BR_REWRITE1;
}
expr *la, *lb, *ra, *rb;

View file

@ -137,6 +137,11 @@ public:
mk_eq(lhs, rhs, r);
return r;
}
expr_ref mk_xor(expr* a, expr* b) {
expr_ref result(m());
mk_xor(a, b, result);
return result;
}
void mk_iff(expr * lhs, expr * rhs, expr_ref & result) { mk_eq(lhs, rhs, result); }
void mk_xor(expr * lhs, expr * rhs, expr_ref & result);
void mk_and(unsigned num_args, expr * const * args, expr_ref & result) {
@ -169,6 +174,16 @@ public:
mk_and(args.size(), args.data(), result);
return result;
}
expr_ref mk_and(expr* a, expr* b) {
expr_ref result(m());
mk_and(a, b, result);
return result;
}
expr_ref mk_or(expr* a, expr* b) {
expr_ref result(m());
mk_or(a, b, result);
return result;
}
void mk_and(expr * arg1, expr * arg2, expr_ref & result) {
expr * args[2] = {arg1, arg2};

View file

@ -136,8 +136,12 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
var * v = nullptr;
expr_ref t(m);
if (m.is_or(e)) {
unsigned num_args = to_app(e)->get_num_args();
if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t))
r = m.mk_false();
else {
expr_ref_vector ors(m);
flatten_or(e, ors);
unsigned num_args = ors.size();
unsigned diseq_count = 0;
unsigned largest_vinx = 0;
@ -149,7 +153,7 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
// Find all disequalities
for (unsigned i = 0; i < num_args; i++) {
if (is_var_diseq(to_app(e)->get_arg(i), num_decls, v, t)) {
if (is_var_diseq(ors.get(i), num_decls, v, t)) {
unsigned idx = v->get_idx();
if (m_map.get(idx, nullptr) == nullptr) {
m_map.reserve(idx + 1);
@ -170,7 +174,7 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
if (!m_order.empty()) {
create_substitution(largest_vinx + 1);
apply_substitution(q, r);
apply_substitution(q, ors, r);
}
}
else {
@ -180,11 +184,6 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) {
}
// Remark: get_elimination_order/top-sort checks for cycles, but it is not invoked for unit clauses.
// So, we must perform a occurs check here.
else if (is_var_diseq(e, num_decls, v, t) && !occurs(v, t)) {
r = m.mk_false();
}
else
r = q;
if (m.proofs_enabled()) {
pr = r == q ? nullptr : m.mk_der(q, r);
@ -327,9 +326,8 @@ void der::create_substitution(unsigned sz) {
}
}
void der::apply_substitution(quantifier * q, expr_ref & r) {
expr * e = q->get_expr();
unsigned num_args=to_app(e)->get_num_args();
void der::apply_substitution(quantifier * q, expr_ref_vector& ors, expr_ref & r) {
unsigned num_args = ors.size();
// get a new expression
m_new_args.reset();
@ -338,13 +336,11 @@ void der::apply_substitution(quantifier * q, expr_ref & r) {
if (x != -1 && m_map.get(x) != nullptr)
continue; // this is a disequality with definition (vanishes)
m_new_args.push_back(to_app(e)->get_arg(i));
m_new_args.push_back(ors.get(i));
}
unsigned sz = m_new_args.size();
expr_ref t(m);
t = (sz == 1) ? m_new_args[0] : m.mk_or(sz, m_new_args.data());
expr_ref new_e = m_subst(t, m_subst_map.size(), m_subst_map.data());
expr_ref t(mk_or(m, m_new_args.size(), m_new_args.data()), m);
expr_ref new_e = m_subst(t, m_subst_map);
// don't forget to update the quantifier patterns
expr_ref_buffer new_patterns(m);

View file

@ -147,7 +147,7 @@ class der {
void get_elimination_order();
void create_substitution(unsigned sz);
void apply_substitution(quantifier * q, expr_ref & r);
void apply_substitution(quantifier * q, expr_ref_vector& ors, expr_ref & r);
void reduce1(quantifier * q, expr_ref & r, proof_ref & pr);

View file

@ -52,21 +52,21 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
arith_util m_a_util;
bv_util m_bv_util;
expr_safe_replace m_rep;
bool m_new_subst { false };
expr_ref_vector m_pinned;
unsigned long long m_max_memory; // in bytes
unsigned m_max_steps;
bool m_pull_cheap_ite;
bool m_flat;
bool m_cache_all;
bool m_push_ite_arith;
bool m_push_ite_bv;
bool m_ignore_patterns_on_ground_qbody;
bool m_rewrite_patterns;
// substitution support
// substitution support
expr_dependency_ref m_used_dependencies; // set of dependencies of used substitutions
expr_substitution * m_subst;
expr_substitution * m_subst = nullptr;
unsigned long long m_max_memory; // in bytes
bool m_new_subst = false;
unsigned m_max_steps = UINT_MAX;
bool m_pull_cheap_ite = true;
bool m_flat = true;
bool m_cache_all = false;
bool m_push_ite_arith = true;
bool m_push_ite_bv = true;
bool m_ignore_patterns_on_ground_qbody = true;
bool m_rewrite_patterns = true;
ast_manager & m() const { return m_b_rw.m(); }
@ -805,8 +805,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
m_bv_util(m),
m_rep(m),
m_pinned(m),
m_used_dependencies(m),
m_subst(nullptr) {
m_used_dependencies(m) {
updt_local_params(p);
}