Skip to content

Commit

Permalink
increase build version, better propagation in euf-egraph, handle assu…
Browse files Browse the repository at this point in the history
…mptions in sat.smt

- increase build version to 4.12.1. This prepares updated release for MacOs-11 build on x86
- move literal propagation mode in euf-egraph to a callback and traversal of equivalence class. Track antecedent by newest equality instead of root. This makes equality propagation to literals have similar behavior as in legacy solver and appears to result in a speedup (10% fewer conflicts on QF_UF/QG-classification/qg5/iso_icl478.smt2 in preliminary testing)
- fix interaction of pre-processing and assumptions. Pre-processing has to freeze assumption literals so they don't get eliminated. This is similar to dependencies that are already frozen.
  • Loading branch information
NikolajBjorner committed Jan 17, 2023
1 parent c8f197d commit 7368f9f
Show file tree
Hide file tree
Showing 22 changed files with 201 additions and 162 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
cmake_minimum_required(VERSION 3.4)

set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake")
project(Z3 VERSION 4.12.0.0 LANGUAGES CXX)
project(Z3 VERSION 4.12.1.0 LANGUAGES CXX)

################################################################################
# Project version
Expand Down
2 changes: 1 addition & 1 deletion scripts/mk_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from mk_util import *

def init_version():
set_version(4, 12, 0, 0) # express a default build version or pick up ci build version
set_version(4, 12, 1, 0) # express a default build version or pick up ci build version

# Z3 Project definition
def init_project_def():
Expand Down
2 changes: 1 addition & 1 deletion scripts/nightly.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
variables:
Major: '4'
Minor: '12'
Patch: '0'
Patch: '1'
AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId)
NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName)

Expand Down
2 changes: 1 addition & 1 deletion scripts/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
trigger: none

variables:
ReleaseVersion: '4.12.0'
ReleaseVersion: '4.12.1'

stages:

Expand Down
49 changes: 27 additions & 22 deletions src/ast/euf/euf_egraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,7 @@ namespace euf {
m_scopes.push_back(m_updates.size());
m_region.push_scope();
m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead()));
m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead()));
}
SASSERT(m_new_lits_qhead <= m_new_lits.size());
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());
}

Expand Down Expand Up @@ -156,12 +154,29 @@ namespace euf {
}

void egraph::add_literal(enode* n, enode* ante) {
/*
if (n->bool_var() == sat::null_bool_var)
return;
TRACE("euf_verbose", tout << "lit: " << n->get_expr_id() << "\n";);
m_new_lits.push_back(enode_pair(n, ante));
m_updates.push_back(update_record(update_record::new_lit()));
*/
if (!ante) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits;
if (!ante)
m_on_propagate_literal(n, ante);
else if (m.is_true(ante->get_expr()) || m.is_false(ante->get_expr())) {
for (enode* k : enode_class(n)) {
if (k != ante) {
//verbose_stream() << "eq: " << k->value() << " " <<ante->value() << "\n";
m_on_propagate_literal(k, ante);
}
}
}
else {
for (enode* k : enode_class(n)) {
if (k->value() != ante->value()) {
//verbose_stream() << "eq: " << k->value() << " " <<ante->value() << "\n";
m_on_propagate_literal(k, ante);
}
}
}
}

void egraph::new_diseq(enode* n) {
Expand Down Expand Up @@ -339,7 +354,6 @@ namespace euf {
num_scopes -= m_num_scopes;
m_num_scopes = 0;

SASSERT(m_new_lits_qhead <= m_new_lits.size());
unsigned old_lim = m_scopes.size() - num_scopes;
unsigned num_updates = m_scopes[old_lim];
auto undo_node = [&]() {
Expand Down Expand Up @@ -378,18 +392,12 @@ namespace euf {
SASSERT(p.r1->get_th_var(p.m_th_id) != null_theory_var);
p.r1->replace_th_var(p.m_old_th_var, p.m_th_id);
break;
case update_record::tag_t::is_new_lit:
m_new_lits.pop_back();
break;
case update_record::tag_t::is_new_th_eq:
m_new_th_eqs.pop_back();
break;
case update_record::tag_t::is_new_th_eq_qhead:
m_new_th_eqs_qhead = p.qhead;
break;
case update_record::tag_t::is_new_lits_qhead:
m_new_lits_qhead = p.qhead;
break;
case update_record::tag_t::is_inconsistent:
m_inconsistent = p.m_inconsistent;
break;
Expand Down Expand Up @@ -424,7 +432,6 @@ namespace euf {
m_region.pop_scope(num_scopes);
m_to_merge.reset();

SASSERT(m_new_lits_qhead <= m_new_lits.size());
SASSERT(m_new_th_eqs_qhead <= m_new_th_eqs.size());

// DEBUG_CODE(invariant(););
Expand Down Expand Up @@ -461,12 +468,6 @@ namespace euf {
std::swap(n1, n2);
}

if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
add_literal(n1, r2);
if (r2->value() != l_undef && n1->value() == l_undef)
add_literal(n1, r2);
else if (r1->value() != l_undef && n2->value() == l_undef)
add_literal(n2, r1);
remove_parents(r1);
push_eq(r1, n1, r2->num_parents());
merge_justification(n1, n2, j);
Expand All @@ -476,6 +477,13 @@ namespace euf {
r2->inc_class_size(r1->class_size());
merge_th_eq(r1, r2);
reinsert_parents(r1, r2);
if (j.is_congruence() && (m.is_false(r2->get_expr()) || m.is_true(r2->get_expr())))
add_literal(n1, r2);
else if (n2->value() != l_undef && n1->value() != n2->value())
add_literal(n1, n2);
else if (n1->value() != l_undef && n2->value() != n1->value())
add_literal(n2, n1);

for (auto& cb : m_on_merge)
cb(r2, r1);
}
Expand Down Expand Up @@ -565,7 +573,6 @@ namespace euf {


bool egraph::propagate() {
SASSERT(m_new_lits_qhead <= m_new_lits.size());
SASSERT(m_num_scopes == 0 || m_to_merge.empty());
force_push();
for (unsigned i = 0; i < m_to_merge.size() && m.limit().inc() && !inconsistent(); ++i) {
Expand All @@ -574,7 +581,6 @@ namespace euf {
}
m_to_merge.reset();
return
(m_new_lits_qhead < m_new_lits.size()) ||
(m_new_th_eqs_qhead < m_new_th_eqs.size()) ||
inconsistent();
}
Expand Down Expand Up @@ -851,7 +857,6 @@ namespace euf {

std::ostream& egraph::display(std::ostream& out) const {
out << "updates " << m_updates.size() << "\n";
out << "newlits " << m_new_lits.size() << " qhead: " << m_new_lits_qhead << "\n";
out << "neweqs " << m_new_th_eqs.size() << " qhead: " << m_new_th_eqs_qhead << "\n";
m_table.display(out);
unsigned max_args = 0;
Expand Down
19 changes: 5 additions & 14 deletions src/ast/euf/euf_egraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,17 @@ namespace euf {
struct toggle_merge_tf {};
struct add_th_var {};
struct replace_th_var {};
struct new_lit {};
struct new_th_eq {};
struct new_th_eq_qhead {};
struct new_lits_qhead {};
struct inconsistent {};
struct value_assignment {};
struct lbl_hash {};
struct lbl_set {};
struct update_children {};
struct set_relevant {};
enum class tag_t { is_set_parent, is_add_node, is_toggle_cgc, is_toggle_merge_tf, 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_add_th_var, is_replace_th_var, is_new_th_eq,
is_lbl_hash, is_new_th_eq_qhead,
is_inconsistent, is_value_assignment, is_lbl_set, is_set_relevant };
tag_t tag;
enode* r1;
Expand Down Expand Up @@ -145,14 +143,10 @@ namespace euf {
tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {}
update_record(enode* n, theory_id id, theory_var v, replace_th_var) :
tag(tag_t::is_replace_th_var), r1(n), n1(nullptr), m_th_id(id), m_old_th_var(v) {}
update_record(new_lit) :
tag(tag_t::is_new_lit), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
update_record(new_th_eq) :
tag(tag_t::is_new_th_eq), r1(nullptr), n1(nullptr), r2_num_parents(0) {}
update_record(unsigned qh, new_th_eq_qhead):
tag(tag_t::is_new_th_eq_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
update_record(unsigned qh, new_lits_qhead):
tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {}
update_record(bool inc, inconsistent) :
tag(tag_t::is_inconsistent), r1(nullptr), n1(nullptr), m_inconsistent(inc) {}
update_record(enode* n, value_assignment) :
Expand Down Expand Up @@ -187,9 +181,7 @@ namespace euf {
enode *m_n1 = nullptr;
enode *m_n2 = nullptr;
justification m_justification;
unsigned m_new_lits_qhead = 0;
unsigned m_new_th_eqs_qhead = 0;
svector<enode_pair> m_new_lits;
svector<th_eq> m_new_th_eqs;
bool_vector m_th_propagates_diseqs;
enode_vector m_todo;
Expand All @@ -198,7 +190,8 @@ namespace euf {
bool m_default_relevant = true;
uint64_t m_congruence_timestamp = 0;

std::vector<std::function<void(enode*,enode*)>> m_on_merge;
std::vector<std::function<void(enode*,enode*)>> m_on_merge;
std::function<void(enode*, enode*)> m_on_propagate_literal;
std::function<void(enode*)> m_on_make;
std::function<void(expr*,expr*,expr*)> m_used_eq;
std::function<void(app*,app*)> m_used_cc;
Expand Down Expand Up @@ -290,11 +283,8 @@ namespace euf {
is an equality consequence.
*/
void add_th_diseq(theory_id id, theory_var v1, theory_var v2, expr* eq);
bool has_literal() const { return m_new_lits_qhead < m_new_lits.size(); }
bool has_th_eq() const { return m_new_th_eqs_qhead < m_new_th_eqs.size(); }
enode_pair get_literal() const { return m_new_lits[m_new_lits_qhead]; }
th_eq get_th_eq() const { return m_new_th_eqs[m_new_th_eqs_qhead]; }
void next_literal() { force_push(); SASSERT(m_new_lits_qhead < m_new_lits.size()); m_new_lits_qhead++; }
void next_th_eq() { force_push(); SASSERT(m_new_th_eqs_qhead < m_new_th_eqs.size()); m_new_th_eqs_qhead++; }

void set_lbl_hash(enode* n);
Expand All @@ -311,6 +301,7 @@ namespace euf {
void set_default_relevant(bool b) { m_default_relevant = b; }

void set_on_merge(std::function<void(enode* root,enode* other)>& on_merge) { m_on_merge.push_back(on_merge); }
void set_on_propagate(std::function<void(enode* lit,enode* ante)>& on_propagate) { m_on_propagate_literal = on_propagate; }
void set_on_make(std::function<void(enode* n)>& on_make) { m_on_make = on_make; }
void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; }
void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; }
Expand Down
2 changes: 1 addition & 1 deletion src/ast/simplifiers/dependent_expr_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ class dependent_expr_state {
void freeze_recfun();
void freeze_lambda();
void freeze_terms(expr* term, bool only_as_array, ast_mark& visited);
void freeze(expr* term);
void freeze(func_decl* f);
struct thaw : public trail {
unsigned sz;
Expand Down Expand Up @@ -89,6 +88,7 @@ class dependent_expr_state {
/**
* Freeze internal functions
*/
void freeze(expr* term);
bool frozen(func_decl* f) const { return m_frozen.is_marked(f); }
bool frozen(expr* f) const { return is_app(f) && m_frozen.is_marked(to_app(f)->get_decl()); }
void freeze_suffix();
Expand Down
27 changes: 23 additions & 4 deletions src/ast/simplifiers/model_reconstruction_trail.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ Module Name:
// substitutions that use variables from the dependent expressions.
// TODO: add filters to skip sections of the trail that do not touch the current free variables.

void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st) {
void model_reconstruction_trail::replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& st) {
ast_mark free_vars;
scoped_ptr<expr_replacer> rp = mk_default_expr_replacer(m, false);
for (unsigned i = qhead; i < st.qtail(); ++i)
for (unsigned i = qhead; i < st.qtail(); ++i)
add_vars(st[i], free_vars);
for (expr* a : assumptions)
add_vars(a, free_vars);

for (auto& t : m_trail) {
if (!t->m_active)
Expand Down Expand Up @@ -63,7 +65,7 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st
mrp.insert(head, t->m_def, t->m_dep);
dependent_expr de(m, t->m_def, nullptr, t->m_dep);
add_vars(de, free_vars);

for (unsigned i = qhead; i < st.qtail(); ++i) {
auto [f, p, dep1] = st[i]();
expr_ref g(m);
Expand All @@ -73,6 +75,15 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st
if (f != g)
st.update(i, dependent_expr(m, g, nullptr, dep2));
}
for (unsigned i = 0; i < assumptions.size(); ++i) {
expr* a = assumptions.get(i);
expr_ref g(m);
expr_dependency_ref dep(m);
mrp(a, nullptr, g, dep);
if (a != g)
assumptions[i] = g;
// ignore dep.
}
continue;
}

Expand Down Expand Up @@ -103,7 +114,15 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st
CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n");
add_vars(d, free_vars);
st.update(i, d);
}
}

for (unsigned i = 0; i < assumptions.size(); ++i) {
expr* a = assumptions.get(i);
auto [g, dep2] = rp->replace_with_dep(a);
if (a != g)
assumptions[i] = g;
// ignore dep.
}
}
}

Expand Down
12 changes: 8 additions & 4 deletions src/ast/simplifiers/model_reconstruction_trail.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,17 @@ class model_reconstruction_trail {
scoped_ptr_vector<entry> m_trail;
unsigned m_trail_index = 0;

void add_vars(dependent_expr const& d, ast_mark& free_vars) {
for (expr* t : subterms::all(expr_ref(d.fml(), d.get_manager())))
void add_vars(expr* e, ast_mark& free_vars) {
for (expr* t : subterms::all(expr_ref(e, m)))
free_vars.mark(t, true);
}

void add_vars(dependent_expr const& d, ast_mark& free_vars) {
add_vars(d.fml(), free_vars);
}

bool intersects(ast_mark const& free_vars, dependent_expr const& d) {
expr_ref term(d.fml(), d.get_manager());
expr_ref term(d.fml(), m);
auto iter = subterms::all(term);
return any_of(iter, [&](expr* t) { return free_vars.is_marked(t); });
}
Expand Down Expand Up @@ -126,7 +130,7 @@ class model_reconstruction_trail {
* register a new depedent expression, update the trail
* by removing substitutions that are not equivalence preserving.
*/
void replay(unsigned qhead, dependent_expr_state& fmls);
void replay(unsigned qhead, expr_ref_vector& assumptions, dependent_expr_state& fmls);

/**
* retrieve the current model converter corresponding to chaining substitutions from the trail.
Expand Down
Loading

0 comments on commit 7368f9f

Please sign in to comment.