From 281fb67d88488dd70136615465902aa625d2cc6d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Oct 2021 20:01:46 -0700 Subject: [PATCH 01/89] unit propagate with fingerprints --- src/ast/euf/euf_egraph.cpp | 1 + src/sat/smt/array_axioms.cpp | 3 +-- src/sat/smt/q_ematch.cpp | 42 +++++++++++++++++++++--------------- src/sat/smt/q_ematch.h | 8 ++++--- src/sat/smt/q_mam.cpp | 31 ++++++++++++++------------ src/sat/smt/q_solver.cpp | 2 +- 6 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index f5ad2a6f4..1f8e14565 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -681,6 +681,7 @@ namespace euf { void egraph::begin_explain() { SASSERT(m_todo.empty()); m_uses_congruence = false; + DEBUG_CODE(for (enode* n : m_nodes) SASSERT(!n->is_marked1());); } void egraph::end_explain() { diff --git a/src/sat/smt/array_axioms.cpp b/src/sat/smt/array_axioms.cpp index 015c4a0c1..d362dacb6 100644 --- a/src/sat/smt/array_axioms.cpp +++ b/src/sat/smt/array_axioms.cpp @@ -621,8 +621,7 @@ namespace array { continue; // arrays used as indices in other arrays have to be treated as shared issue #3532, #3529 if (ctx.is_shared(r) || is_shared_arg(r)) - roots.push_back(r->get_th_var(get_id())); - + roots.push_back(r->get_th_var(get_id())); r->mark1(); to_unmark.push_back(r); } diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 2d8ac8afd..56ab26093 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -218,11 +218,17 @@ namespace q { } }; - binding* ematch::alloc_binding(unsigned n, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top) { - unsigned sz = sizeof(binding) + sizeof(euf::enode* const*)*n; + + binding* ematch::alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top) { + unsigned n = c.num_decls(); + unsigned sz = sizeof(binding) + sizeof(euf::enode* const*) * n; void* mem = ctx.get_region().allocate(sz); - return new (mem) binding(pat, max_generation, min_top, max_top); - } + binding* b = new (mem) binding(pat, max_generation, min_top, max_top); + b->init(b); + for (unsigned i = 0; i < n; ++i) + b->m_nodes[i] = _binding[i]; + return b; + } euf::enode* const* ematch::alloc_binding(clause& c, euf::enode* const* _binding) { unsigned sz = sizeof(euf::enode* const*) * c.num_decls(); @@ -232,24 +238,22 @@ namespace q { return binding; } - void ematch::add_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top) { - unsigned n = c.num_decls(); - binding* b = alloc_binding(n, pat, max_generation, min_top, max_top); - b->init(b); - for (unsigned i = 0; i < n; ++i) - b->m_nodes[i] = _binding[i]; - binding::push_to_front(c.m_bindings, b); - ctx.push(remove_binding(ctx, c, b)); - ++m_stats.m_num_delayed_bindings; - } - void ematch::on_binding(quantifier* q, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_gen, unsigned max_gen) { TRACE("q", tout << "on-binding " << mk_pp(q, m) << "\n";); unsigned idx = m_q2clauses[q]; clause& c = *m_clauses[idx]; bool new_propagation = false; - if (!propagate(false, _binding, max_generation, c, new_propagation)) - add_binding(c, pat, _binding, max_generation, min_gen, max_gen); + binding* b = alloc_binding(c, pat, _binding, max_generation, min_gen, max_gen); + fingerprint* f = add_fingerprint(c, *b, max_generation); + if (!f) + return; + + if (propagate(false, _binding, max_generation, c, new_propagation)) + return; + + binding::push_to_front(c.m_bindings, b); + ctx.push(remove_binding(ctx, c, b)); + ++m_stats.m_num_delayed_bindings; } bool ematch::propagate(bool is_owned, euf::enode* const* binding, unsigned max_generation, clause& c, bool& propagated) { @@ -547,6 +551,10 @@ namespace q { } + bool ematch::unit_propagate() { + return ctx.get_config().m_ematching && propagate(false); + } + bool ematch::propagate(bool flush) { m_mam->propagate(); bool propagated = flush_prop_queue(); diff --git a/src/sat/smt/q_ematch.h b/src/sat/smt/q_ematch.h index 41e844572..fbedbd65a 100644 --- a/src/sat/smt/q_ematch.h +++ b/src/sat/smt/q_ematch.h @@ -90,10 +90,10 @@ namespace q { unsigned_vector m_clause_queue; euf::enode_pair_vector m_evidence; - binding* alloc_binding(unsigned n, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top); euf::enode* const* alloc_binding(clause& c, euf::enode* const* _binding); + binding* alloc_binding(clause& c, app* pat, euf::enode* const* _bidning, unsigned max_generation, unsigned min_top, unsigned max_top); void add_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top); - + sat::ext_justification_idx mk_justification(unsigned idx, clause& c, euf::enode* const* b); void ensure_ground_enodes(expr* e); @@ -121,13 +121,15 @@ namespace q { bool flush_prop_queue(); void propagate(bool is_conflict, unsigned idx, sat::ext_justification_idx j_idx); + bool propagate(bool flush); + public: ematch(euf::solver& ctx, solver& s); bool operator()(); - bool propagate(bool flush); + bool unit_propagate(); // void init_search(); diff --git a/src/sat/smt/q_mam.cpp b/src/sat/smt/q_mam.cpp index 7999ecea9..6581d271d 100644 --- a/src/sat/smt/q_mam.cpp +++ b/src/sat/smt/q_mam.cpp @@ -1987,33 +1987,36 @@ namespace q { m_backtrack_stack.resize(t->get_num_choices()); } + struct scoped_unmark { + code_tree* t; + scoped_unmark(code_tree* t) : t(t) {} + ~scoped_unmark() { + for (enode* app : t->get_candidates()) + if (app->is_marked1()) + app->unmark1(); + } + }; + void execute(code_tree * t) { TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout);); init(t); - if (t->filter_candidates()) { - for (unsigned i = 0; i < t->get_candidates().size(); ++i) { + if (t->filter_candidates()) { + scoped_unmark _unmark(t); + for (unsigned i = 0; i < t->get_candidates().size() && !ctx.resource_limits_exceeded(); ++i) { enode* app = t->get_candidates()[i]; TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_expr(), m) << "\n";); if (!app->is_marked1() && app->is_cgr()) { - if (ctx.resource_limits_exceeded() || !execute_core(t, app)) - return; + execute_core(t, app); app->mark1(); } } - for (enode* app : t->get_candidates()) { - if (app->is_marked1()) - app->unmark1(); - } } else { - for (unsigned i = 0; i < t->get_candidates().size(); ++i) { + for (unsigned i = 0; i < t->get_candidates().size() && !ctx.resource_limits_exceeded(); ++i) { enode* app = t->get_candidates()[i]; TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_expr(), m) << "\n";); - if (app->is_cgr()) { - TRACE("trigger_bug", tout << "is_cgr\n";); - if (ctx.resource_limits_exceeded() || !execute_core(t, app)) - return; - } + if (app->is_cgr()) + execute_core(t, app); } } } diff --git a/src/sat/smt/q_solver.cpp b/src/sat/smt/q_solver.cpp index 480853dbd..148383c2b 100644 --- a/src/sat/smt/q_solver.cpp +++ b/src/sat/smt/q_solver.cpp @@ -100,7 +100,7 @@ namespace q { } bool solver::unit_propagate() { - return ctx.get_config().m_ematching && m_ematch.propagate(false); + return m_ematch.unit_propagate(); } euf::theory_var solver::mk_var(euf::enode* n) { From 33f4e65fa919349501e7511669131f6742fc6b1b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Oct 2021 10:15:56 -0700 Subject: [PATCH 02/89] redo bindings/fingerprints Signed-off-by: Nikolaj Bjorner --- src/sat/smt/q_clause.h | 87 ++++++++++++++++++++++------- src/sat/smt/q_ematch.cpp | 107 ++++++++++++++++++------------------ src/sat/smt/q_ematch.h | 14 ++--- src/sat/smt/q_fingerprint.h | 77 -------------------------- src/sat/smt/q_queue.cpp | 22 ++++---- src/sat/smt/q_queue.h | 18 +++--- 6 files changed, 145 insertions(+), 180 deletions(-) delete mode 100644 src/sat/smt/q_fingerprint.h diff --git a/src/sat/smt/q_clause.h b/src/sat/smt/q_clause.h index 66daf07ea..08a6f615a 100644 --- a/src/sat/smt/q_clause.h +++ b/src/sat/smt/q_clause.h @@ -22,6 +22,7 @@ Author: #include "ast/euf/euf_enode.h" #include "sat/smt/euf_solver.h" + namespace q { struct lit { @@ -35,14 +36,40 @@ namespace q { std::ostream& display(std::ostream& out) const; }; + struct binding; + + struct clause { + unsigned m_index; + vector m_lits; + quantifier_ref m_q; + unsigned m_watch = 0; + sat::literal m_literal = sat::null_literal; + q::quantifier_stat* m_stat = nullptr; + binding* m_bindings = nullptr; + + + clause(ast_manager& m, unsigned idx) : m_index(idx), m_q(m) {} + + std::ostream& display(euf::solver& ctx, std::ostream& out) const; + lit const& operator[](unsigned i) const { return m_lits[i]; } + lit& operator[](unsigned i) { return m_lits[i]; } + unsigned size() const { return m_lits.size(); } + unsigned num_decls() const { return m_q->get_num_decls(); } + unsigned index() const { return m_index; } + quantifier* q() const { return m_q; } + }; + + struct binding : public dll_base { + clause* c; app* m_pattern; unsigned m_max_generation; unsigned m_min_top_generation; unsigned m_max_top_generation; euf::enode* m_nodes[0]; - binding(app* pat, unsigned max_generation, unsigned min_top, unsigned max_top): + binding(clause& c, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top): + c(&c), m_pattern(pat), m_max_generation(max_generation), m_min_top_generation(min_top), @@ -53,29 +80,49 @@ namespace q { euf::enode* operator[](unsigned i) const { return m_nodes[i]; } std::ostream& display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const; + + unsigned size() const { return c->num_decls(); } + + quantifier* q() const { return c->m_q; } + + bool eq(binding const& other) const { + if (q() != other.q()) + return false; + for (unsigned i = size(); i-- > 0; ) + if ((*this)[i] != other[i]) + return false; + return true; + } }; - struct clause { - unsigned m_index; - vector m_lits; - quantifier_ref m_q; - unsigned m_watch = 0; - sat::literal m_literal = sat::null_literal; - q::quantifier_stat* m_stat = nullptr; - binding* m_bindings = nullptr; - - - clause(ast_manager& m, unsigned idx): m_index(idx), m_q(m) {} - - std::ostream& display(euf::solver& ctx, std::ostream& out) const; - lit const& operator[](unsigned i) const { return m_lits[i]; } - lit& operator[](unsigned i) { return m_lits[i]; } - unsigned size() const { return m_lits.size(); } - unsigned num_decls() const { return m_q->get_num_decls(); } - unsigned index() const { return m_index; } - quantifier* q() const { return m_q; } + struct binding_khasher { + unsigned operator()(binding const* f) const { return f->q()->get_id(); } }; + struct binding_chasher { + unsigned operator()(binding const* f, unsigned idx) const { return f->m_nodes[idx]->hash(); } + }; + + struct binding_hash_proc { + unsigned operator()(binding const* f) const { + return get_composite_hash(const_cast(f), f->size()); + } + }; + + struct binding_eq_proc { + bool operator()(binding const* a, binding const* b) const { return a->eq(*b); } + }; + + typedef ptr_hashtable bindings; + + inline std::ostream& operator<<(std::ostream& out, binding const& f) { + out << "[fp " << f.q()->get_id() << ":"; + for (unsigned i = 0; i < f.size(); ++i) + out << " " << f[i]->get_expr_id(); + return out << "]"; + } + + struct justification { expr* m_lhs, *m_rhs; bool m_sign; diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 56ab26093..1cfc1f678 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -219,18 +219,48 @@ namespace q { }; + + binding* ematch::tmp_binding(clause& c, app* pat, euf::enode* const* b) { + if (c.num_decls() > m_tmp_binding_capacity) { + void* mem = memory::allocate(sizeof(binding) + c.num_decls() * sizeof(euf::enode*)); + m_tmp_binding = new (mem) binding(c, pat, 0, 0, 0); + m_tmp_binding_capacity = c.num_decls(); + } + + for (unsigned i = c.num_decls(); i-- > 0; ) + m_tmp_binding->m_nodes[i] = b[i]; + m_tmp_binding->m_pattern = pat; + m_tmp_binding->c = &c; + + return m_tmp_binding.get(); + } + binding* ematch::alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top) { + binding* b = tmp_binding(c, pat, _binding); + + if (m_bindings.contains(b)) + return nullptr; + + for (unsigned i = c.num_decls(); i-- > 0; ) + b->m_nodes[i] = b->m_nodes[i]->get_root(); + + if (m_bindings.contains(b)) + return nullptr; + unsigned n = c.num_decls(); unsigned sz = sizeof(binding) + sizeof(euf::enode* const*) * n; void* mem = ctx.get_region().allocate(sz); - binding* b = new (mem) binding(pat, max_generation, min_top, max_top); + b = new (mem) binding(c, pat, max_generation, min_top, max_top); b->init(b); for (unsigned i = 0; i < n; ++i) b->m_nodes[i] = _binding[i]; + + m_bindings.insert(b); + ctx.push(insert_map(m_bindings, b)); return b; } - euf::enode* const* ematch::alloc_binding(clause& c, euf::enode* const* _binding) { + euf::enode* const* ematch::alloc_nodes(clause& c, euf::enode* const* _binding) { unsigned sz = sizeof(euf::enode* const*) * c.num_decls(); euf::enode** binding = (euf::enode**)ctx.get_region().allocate(sz); for (unsigned i = 0; i < c.num_decls(); ++i) @@ -244,8 +274,7 @@ namespace q { clause& c = *m_clauses[idx]; bool new_propagation = false; binding* b = alloc_binding(c, pat, _binding, max_generation, min_gen, max_gen); - fingerprint* f = add_fingerprint(c, *b, max_generation); - if (!f) + if (!b) return; if (propagate(false, _binding, max_generation, c, new_propagation)) @@ -276,7 +305,7 @@ namespace q { if (ev == l_undef && max_generation > m_generation_propagation_threshold) return false; if (!is_owned) - binding = alloc_binding(c, binding); + binding = alloc_nodes(c, binding); auto j_idx = mk_justification(idx, c, binding); @@ -312,17 +341,14 @@ namespace q { return true; } - void ematch::instantiate(binding& b, clause& c) { + void ematch::instantiate(binding& b) { if (m_stats.m_num_instantiations > ctx.get_config().m_qi_max_instances) return; unsigned max_generation = b.m_max_generation; - max_generation = std::max(max_generation, c.m_stat->get_generation()); - c.m_stat->update_max_generation(max_generation); - fingerprint * f = add_fingerprint(c, b, max_generation); - if (!f) - return; - m_inst_queue.insert(f); - m_stats.m_num_instantiations++; + max_generation = std::max(max_generation, b.c->m_stat->get_generation()); + b.c->m_stat->update_max_generation(max_generation); + m_stats.m_num_instantiations++; + m_inst_queue.insert(&b); } void ematch::add_instantiation(clause& c, binding& b, sat::literal lit) { @@ -330,35 +356,6 @@ namespace q { ctx.propagate(lit, mk_justification(UINT_MAX, c, b.nodes())); } - void ematch::set_tmp_binding(fingerprint& fp) { - binding& b = *fp.b; - clause& c = *fp.c; - if (c.num_decls() > m_tmp_binding_capacity) { - void* mem = memory::allocate(sizeof(binding) + c.num_decls()*sizeof(euf::enode*)); - m_tmp_binding = new (mem) binding(b.m_pattern, 0, 0, 0); - m_tmp_binding_capacity = c.num_decls(); - } - - fp.b = m_tmp_binding.get(); - for (unsigned i = c.num_decls(); i-- > 0; ) - fp.b->m_nodes[i] = b[i]; - } - - fingerprint* ematch::add_fingerprint(clause& c, binding& b, unsigned max_generation) { - fingerprint fp(c, b, max_generation); - if (m_fingerprints.contains(&fp)) - return nullptr; - set_tmp_binding(fp); - for (unsigned i = c.num_decls(); i-- > 0; ) - fp.b->m_nodes[i] = fp.b->m_nodes[i]->get_root(); - if (m_fingerprints.contains(&fp)) - return nullptr; - fingerprint* f = new (ctx.get_region()) fingerprint(c, b, max_generation); - m_fingerprints.insert(f); - ctx.push(insert_map(m_fingerprints, f)); - return f; - } - sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) { expr_ref_vector _binding(m); for (unsigned i = 0; i < c.num_decls(); ++i) @@ -552,6 +549,7 @@ namespace q { bool ematch::unit_propagate() { + return false; return ctx.get_config().m_ematching && propagate(false); } @@ -569,12 +567,13 @@ namespace q { if (!b) continue; - do { + do { if (propagate(true, b->m_nodes, b->m_max_generation, c, propagated)) to_remove.push_back(b); else if (flush) { - instantiate(*b, c); + instantiate(*b); to_remove.push_back(b); + propagated = true; } b = b->next(); } @@ -600,21 +599,21 @@ namespace q { TRACE("q", m_mam->display(tout);); if (propagate(false)) return true; - if (m_lazy_mam) { + if (m_lazy_mam) m_lazy_mam->propagate(); - if (propagate(false)) - return true; - } - unsigned idx = 0; - for (clause* c : m_clauses) { - if (c->m_bindings) - insert_clause_in_queue(idx); - idx++; - } + if (propagate(false)) + return true; + for (unsigned i = 0; i < m_clauses.size(); ++i) + if (m_clauses[i]->m_bindings) + insert_clause_in_queue(i); if (propagate(true)) return true; if (m_inst_queue.lazy_propagate()) return true; + for (unsigned i = 0; i < m_clauses.size(); ++i) + if (m_clauses[i]->m_bindings) + std::cout << "missed propagation " << i << "\n"; + TRACE("q", tout << "no more propagation\n";); return false; } diff --git a/src/sat/smt/q_ematch.h b/src/sat/smt/q_ematch.h index fbedbd65a..bd79511a8 100644 --- a/src/sat/smt/q_ematch.h +++ b/src/sat/smt/q_ematch.h @@ -23,7 +23,6 @@ Author: #include "sat/smt/sat_th.h" #include "sat/smt/q_mam.h" #include "sat/smt/q_clause.h" -#include "sat/smt/q_fingerprint.h" #include "sat/smt/q_queue.h" #include "sat/smt/q_eval.h" @@ -69,7 +68,7 @@ namespace q { ast_manager& m; eval m_eval; quantifier_stat_gen m_qstat_gen; - fingerprints m_fingerprints; + bindings m_bindings; scoped_ptr m_tmp_binding; unsigned m_tmp_binding_capacity = 0; queue m_inst_queue; @@ -90,16 +89,16 @@ namespace q { unsigned_vector m_clause_queue; euf::enode_pair_vector m_evidence; - euf::enode* const* alloc_binding(clause& c, euf::enode* const* _binding); - binding* alloc_binding(clause& c, app* pat, euf::enode* const* _bidning, unsigned max_generation, unsigned min_top, unsigned max_top); - void add_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top); + euf::enode* const* alloc_nodes(clause& c, euf::enode* const* _binding); + binding* tmp_binding(clause& c, app* pat, euf::enode* const* _binding); + binding* alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top); sat::ext_justification_idx mk_justification(unsigned idx, clause& c, euf::enode* const* b); void ensure_ground_enodes(expr* e); void ensure_ground_enodes(clause const& c); - void instantiate(binding& b, clause& c); + void instantiate(binding& b); sat::literal instantiate(clause& c, euf::enode* const* binding, lit const& l); // register as callback into egraph. @@ -115,9 +114,6 @@ namespace q { clause* clausify(quantifier* q); lit clausify_literal(expr* arg); - fingerprint* add_fingerprint(clause& c, binding& b, unsigned max_generation); - void set_tmp_binding(fingerprint& fp); - bool flush_prop_queue(); void propagate(bool is_conflict, unsigned idx, sat::ext_justification_idx j_idx); diff --git a/src/sat/smt/q_fingerprint.h b/src/sat/smt/q_fingerprint.h deleted file mode 100644 index 99ad602b9..000000000 --- a/src/sat/smt/q_fingerprint.h +++ /dev/null @@ -1,77 +0,0 @@ -/*++ -Copyright (c) 2020 Microsoft Corporation - -Module Name: - - q_fingerprint.h - -Abstract: - - Fingerprint summary of a quantifier instantiation - -Author: - - Nikolaj Bjorner (nbjorner) 2021-01-24 - ---*/ -#pragma once - -#include "util/hashtable.h" -#include "ast/ast.h" -#include "ast/quantifier_stat.h" -#include "ast/euf/euf_enode.h" -#include "sat/smt/q_clause.h" - - -namespace q { - - struct fingerprint { - clause* c; - binding* b; - unsigned m_max_generation; - - unsigned size() const { return c->num_decls(); } - euf::enode* const* nodes() const { return b->nodes(); } - quantifier* q() const { return c->m_q; } - - fingerprint(clause& _c, binding& _b, unsigned mg) : - c(&_c), b(&_b), m_max_generation(mg) {} - - bool eq(fingerprint const& other) const { - if (c->m_q != other.c->m_q) - return false; - for (unsigned i = size(); i--> 0; ) - if ((*b)[i] != (*other.b)[i]) - return false; - return true; - } - }; - - struct fingerprint_khasher { - unsigned operator()(fingerprint const * f) const { return f->c->m_q->get_id(); } - }; - - struct fingerprint_chasher { - unsigned operator()(fingerprint const * f, unsigned idx) const { return f->b->m_nodes[idx]->hash(); } - }; - - struct fingerprint_hash_proc { - unsigned operator()(fingerprint const * f) const { - return get_composite_hash(const_cast(f), f->size()); - } - }; - - struct fingerprint_eq_proc { - bool operator()(fingerprint const* a, fingerprint const* b) const { return a->eq(*b); } - }; - - typedef ptr_hashtable fingerprints; - - inline std::ostream& operator<<(std::ostream& out, fingerprint const& f) { - out << "[fp " << f.q()->get_id() << ":"; - for (unsigned i = 0; i < f.size(); ++i) - out << " " << (*f.b)[i]->get_expr_id(); - return out << "]"; - } - -} diff --git a/src/sat/smt/q_queue.cpp b/src/sat/smt/q_queue.cpp index 247451fb4..2e8db482f 100644 --- a/src/sat/smt/q_queue.cpp +++ b/src/sat/smt/q_queue.cpp @@ -86,13 +86,13 @@ namespace q { m_parser.add_var("cs_factor"); } - void queue::set_values(fingerprint& f, float cost) { + void queue::set_values(binding& f, float cost) { quantifier_stat * stat = f.c->m_stat; quantifier* q = f.q(); - app* pat = f.b->m_pattern; + app* pat = f.m_pattern; m_vals[COST] = cost; - m_vals[MIN_TOP_GENERATION] = static_cast(f.b->m_min_top_generation); - m_vals[MAX_TOP_GENERATION] = static_cast(f.b->m_max_top_generation); + m_vals[MIN_TOP_GENERATION] = static_cast(f.m_min_top_generation); + m_vals[MAX_TOP_GENERATION] = static_cast(f.m_max_top_generation); m_vals[INSTANCES] = static_cast(stat->get_num_instances_curr_branch()); m_vals[SIZE] = static_cast(stat->get_size()); m_vals[DEPTH] = static_cast(stat->get_depth()); @@ -108,14 +108,14 @@ namespace q { TRACE("q_detail", for (unsigned i = 0; i < m_vals.size(); i++) { tout << m_vals[i] << " "; } tout << "\n";); } - float queue::get_cost(fingerprint& f) { + float queue::get_cost(binding& f) { set_values(f, 0); float r = m_evaluator(m_cost_function, m_vals.size(), m_vals.data()); f.c->m_stat->update_max_cost(r); return r; } - unsigned queue::get_new_gen(fingerprint& f, float cost) { + unsigned queue::get_new_gen(binding& f, float cost) { set_values(f, cost); float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.data()); return std::max(f.m_max_generation + 1, static_cast(r)); @@ -129,7 +129,7 @@ namespace q { } }; - void queue::insert(fingerprint* f) { + void queue::insert(binding* f) { float cost = get_cost(*f); if (m_new_entries.empty()) ctx.push(reset_new_entries(m_new_entries)); @@ -137,7 +137,7 @@ namespace q { } void queue::instantiate(entry& ent) { - fingerprint & f = *ent.m_qb; + binding& f = *ent.m_qb; quantifier * q = f.q(); unsigned num_bindings = f.size(); quantifier_stat * stat = f.c->m_stat; @@ -151,7 +151,7 @@ namespace q { auto* ebindings = m_subst(q, num_bindings); for (unsigned i = 0; i < num_bindings; ++i) - ebindings[i] = f.nodes()[i]->get_expr(); + ebindings[i] = f[i]->get_expr(); expr_ref instance = m_subst(); ctx.get_rewriter()(instance); if (m.is_true(instance)) { @@ -164,7 +164,7 @@ namespace q { euf::solver::scoped_generation _sg(ctx, gen); sat::literal result_l = ctx.mk_literal(instance); - em.add_instantiation(*f.c, *f.b, result_l); + em.add_instantiation(*f.c, f, result_l); } bool queue::propagate() { @@ -178,7 +178,7 @@ namespace q { if (0 == since_last_check && ctx.resource_limits_exceeded()) break; - fingerprint& f = *curr.m_qb; + binding& f = *curr.m_qb; if (curr.m_cost <= m_eager_cost_threshold) instantiate(curr); diff --git a/src/sat/smt/q_queue.h b/src/sat/smt/q_queue.h index c23cb0377..3750ee31b 100644 --- a/src/sat/smt/q_queue.h +++ b/src/sat/smt/q_queue.h @@ -20,7 +20,7 @@ Author: #include "ast/cost_evaluator.h" #include "ast/rewriter/cached_var_subst.h" #include "parsers/util/cost_parser.h" -#include "sat/smt/q_fingerprint.h" +#include "sat/smt/q_clause.h" @@ -51,12 +51,12 @@ namespace q { cost_evaluator m_evaluator; cached_var_subst m_subst; svector m_vals; - double m_eager_cost_threshold { 0 }; + double m_eager_cost_threshold = 0; struct entry { - fingerprint * m_qb; + binding * m_qb; float m_cost; - bool m_instantiated{ false }; - entry(fingerprint * f, float c):m_qb(f), m_cost(c) {} + bool m_instantiated = false; + entry(binding * f, float c):m_qb(f), m_cost(c) {} }; struct reset_new_entries; struct reset_instantiated; @@ -64,18 +64,18 @@ namespace q { svector m_new_entries; svector m_delayed_entries; - float get_cost(fingerprint& f); - void set_values(fingerprint& f, float cost); + float get_cost(binding& f); + void set_values(binding& f, float cost); void init_parser_vars(); void setup(); - unsigned get_new_gen(fingerprint& f, float cost); + unsigned get_new_gen(binding& f, float cost); void instantiate(entry& e); public: queue(ematch& em, euf::solver& ctx); - void insert(fingerprint* f); + void insert(binding* f); bool propagate(); From 94cc4ead7249af4b8a9fbca635484f5a3b428a99 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Oct 2021 10:55:38 -0700 Subject: [PATCH 03/89] remove arith_lhs simplification from preamble tactic Signed-off-by: Nikolaj Bjorner --- src/sat/smt/q_ematch.cpp | 4 +- src/tactic/smtlogics/qflia_tactic.cpp | 54 ++++++++++++--------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 1cfc1f678..6f200680a 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -80,7 +80,7 @@ namespace q { unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) ensure_ground_enodes(q->get_pattern(i)); - for (auto lit : c.m_lits) { + for (auto const& lit : c.m_lits) { ensure_ground_enodes(lit.lhs); ensure_ground_enodes(lit.rhs); } @@ -186,7 +186,7 @@ namespace q { void ematch::init_watch(clause& c) { unsigned idx = c.index(); - for (auto lit : c.m_lits) { + for (auto const& lit : c.m_lits) { if (!is_ground(lit.lhs)) init_watch(lit.lhs, idx); if (!is_ground(lit.rhs)) diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index 8673b6380..b8ebbd8a9 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -190,22 +190,14 @@ tactic * mk_preamble_tactic(ast_manager& m) { ctx_simp_p.set_uint("max_depth", 30); ctx_simp_p.set_uint("max_steps", 5000000); - params_ref lhs_p; - lhs_p.set_bool("arith_lhs", true); - - tactic * preamble_st = and_then(mk_simplify_tactic(m), - mk_propagate_values_tactic(m), - using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), - using_params(mk_simplify_tactic(m), pull_ite_p) - ); - - preamble_st = and_then(preamble_st, - mk_solve_eqs_tactic(m), - mk_elim_uncnstr_tactic(m), - using_params(mk_simplify_tactic(m), lhs_p) - ); - - return preamble_st; + return + and_then( + mk_simplify_tactic(m), + mk_propagate_values_tactic(m), + using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), + using_params(mk_simplify_tactic(m), pull_ite_p), + mk_solve_eqs_tactic(m), + mk_elim_uncnstr_tactic(m)); } tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { @@ -215,23 +207,25 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { main_p.set_bool("blast_distinct", true); main_p.set_uint("blast_distinct_threshold", 128); // main_p.set_bool("push_ite_arith", true); - - - + params_ref quasi_pb_p; quasi_pb_p.set_uint("lia2pb_max_bits", 64); - - tactic * preamble_st = mk_preamble_tactic(m); - tactic * st = using_params(and_then(preamble_st, - or_else(mk_ilp_model_finder_tactic(m), - mk_pb_tactic(m), - and_then(fail_if_not(mk_is_quasi_pb_probe()), - using_params(mk_lia2sat_tactic(m), quasi_pb_p), - mk_fail_if_undecided_tactic()), - mk_bounded_tactic(m), - mk_smt_tactic(m))), - main_p); + params_ref lhs_p; + lhs_p.set_bool("arith_lhs", true); + + tactic* st = using_params( + and_then( + mk_preamble_tactic(m), + using_params(mk_simplify_tactic(m), lhs_p), + or_else(mk_ilp_model_finder_tactic(m), + mk_pb_tactic(m), + and_then(fail_if_not(mk_is_quasi_pb_probe()), + using_params(mk_lia2sat_tactic(m), quasi_pb_p), + mk_fail_if_undecided_tactic()), + mk_bounded_tactic(m), + mk_smt_tactic(m))), + main_p); st->updt_params(p); From c0c3e685e7801c7d670c11613f3cc8f4b293c724 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 5 Oct 2021 11:25:35 -0700 Subject: [PATCH 04/89] disable all propagation until ematch incompleteness is fixed Signed-off-by: Nikolaj Bjorner --- src/sat/smt/q_ematch.cpp | 18 ++++++++++-------- src/sat/smt/q_ematch.h | 3 +-- src/sat/smt/q_queue.cpp | 7 +++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 6f200680a..942c070fd 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -236,9 +236,10 @@ namespace q { } binding* ematch::alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top) { + binding* b = tmp_binding(c, pat, _binding); - if (m_bindings.contains(b)) + if (m_bindings.contains(b)) return nullptr; for (unsigned i = c.num_decls(); i-- > 0; ) @@ -260,12 +261,12 @@ namespace q { return b; } - euf::enode* const* ematch::alloc_nodes(clause& c, euf::enode* const* _binding) { + euf::enode* const* ematch::copy_nodes(clause& c, euf::enode* const* nodes) { unsigned sz = sizeof(euf::enode* const*) * c.num_decls(); - euf::enode** binding = (euf::enode**)ctx.get_region().allocate(sz); + euf::enode** new_nodes = (euf::enode**)ctx.get_region().allocate(sz); for (unsigned i = 0; i < c.num_decls(); ++i) - binding[i] = _binding[i]; - return binding; + new_nodes[i] = nodes[i]; + return new_nodes; } void ematch::on_binding(quantifier* q, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_gen, unsigned max_gen) { @@ -277,7 +278,7 @@ namespace q { if (!b) return; - if (propagate(false, _binding, max_generation, c, new_propagation)) + if (false && propagate(false, _binding, max_generation, c, new_propagation)) return; binding::push_to_front(c.m_bindings, b); @@ -305,7 +306,7 @@ namespace q { if (ev == l_undef && max_generation > m_generation_propagation_threshold) return false; if (!is_owned) - binding = alloc_nodes(c, binding); + binding = copy_nodes(c, binding); auto j_idx = mk_justification(idx, c, binding); @@ -568,7 +569,7 @@ namespace q { continue; do { - if (propagate(true, b->m_nodes, b->m_max_generation, c, propagated)) + if (false && propagate(true, b->m_nodes, b->m_max_generation, c, propagated)) to_remove.push_back(b); else if (flush) { instantiate(*b); @@ -613,6 +614,7 @@ namespace q { for (unsigned i = 0; i < m_clauses.size(); ++i) if (m_clauses[i]->m_bindings) std::cout << "missed propagation " << i << "\n"; + TRACE("q", tout << "no more propagation\n";); return false; } diff --git a/src/sat/smt/q_ematch.h b/src/sat/smt/q_ematch.h index bd79511a8..58037e308 100644 --- a/src/sat/smt/q_ematch.h +++ b/src/sat/smt/q_ematch.h @@ -89,7 +89,7 @@ namespace q { unsigned_vector m_clause_queue; euf::enode_pair_vector m_evidence; - euf::enode* const* alloc_nodes(clause& c, euf::enode* const* _binding); + euf::enode* const* copy_nodes(clause& c, euf::enode* const* _binding); binding* tmp_binding(clause& c, app* pat, euf::enode* const* _binding); binding* alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top); @@ -127,7 +127,6 @@ namespace q { bool unit_propagate(); - // void init_search(); void add(quantifier* q); diff --git a/src/sat/smt/q_queue.cpp b/src/sat/smt/q_queue.cpp index 2e8db482f..c534c9f80 100644 --- a/src/sat/smt/q_queue.cpp +++ b/src/sat/smt/q_queue.cpp @@ -146,7 +146,7 @@ namespace q { unsigned gen = get_new_gen(f, ent.m_cost); bool new_propagation = false; - if (em.propagate(true, f.nodes(), gen, *f.c, new_propagation)) + if (false && em.propagate(true, f.nodes(), gen, *f.c, new_propagation)) return; auto* ebindings = m_subst(q, num_bindings); @@ -223,15 +223,14 @@ namespace q { } } bool instantiated = false; - unsigned idx = 0; - for (entry & e : m_delayed_entries) { + for (unsigned idx = 0; idx < m_delayed_entries.size(); ++idx) { + entry & e = m_delayed_entries[idx]; if (!e.m_instantiated && e.m_cost <= cost_limit) { instantiated = true; ctx.push(reset_instantiated(*this, idx)); m_stats.m_num_lazy_instances++; instantiate(e); } - ++idx; } return instantiated; } From 146f4621c54d2755f75f51ab37dee42eff9f81e6 Mon Sep 17 00:00:00 2001 From: Margus Veanes Date: Fri, 8 Oct 2021 13:04:49 -0700 Subject: [PATCH 05/89] Updated regex derivative engine (#5567) * updated derivative engine * some edit * further improvements in derivative code * more deriv code edits and re::to_str update * optimized mk_deriv_accept * fixed PR comments * small syntax fix * updated some simplifications * bugfix:forgot to_re before reverse * fixed PR comments * more PR comment fixes * more PR comment fixes * forgot to delete * deleting unused definition * fixes Signed-off-by: Nikolaj Bjorner * fixes Signed-off-by: Nikolaj Bjorner Co-authored-by: Nikolaj Bjorner --- src/ast/rewriter/seq_axioms.cpp | 2 +- src/ast/rewriter/seq_rewriter.cpp | 529 ++++++++++++++++++++++++++---- src/ast/rewriter/seq_rewriter.h | 46 ++- src/ast/seq_decl_plugin.cpp | 335 ++++++++++++++----- src/ast/seq_decl_plugin.h | 48 ++- src/smt/seq_regex.cpp | 209 ++++++------ src/smt/seq_regex.h | 4 +- 7 files changed, 893 insertions(+), 280 deletions(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index bcda71e54..14e6290c6 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -850,7 +850,7 @@ namespace seq { add_clause(~eq, ge10k); for (unsigned i = 0; i < k; ++i) { - expr* ch = seq.str.mk_nth_i(ubvs, i); + expr* ch = seq.str.mk_nth_c(ubvs, i); is_digit = seq.mk_char_is_digit(ch); add_clause(~ge_len, is_digit); } diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 63a5044e5..a956718dc 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -859,13 +859,12 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { // elif offset >= len(s) then 0 // elif offset + length > len(s) then len(s) - offset // else length - expr_ref zero(m_autil.mk_int(0), m()); result = length; result = m().mk_ite(m_autil.mk_gt(m_autil.mk_add(offset, length), len_s), m_autil.mk_sub(len_s, offset), result); - result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero), m_autil.mk_lt(offset, zero)), - zero, + result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero()), m_autil.mk_lt(offset, zero())), + zero(), result); return BR_REWRITE_FULL; } @@ -883,37 +882,56 @@ expr_ref seq_rewriter::mk_seq_first(expr* t) { if (str().is_extract(t, s, j, k)) result = str().mk_nth_i(s, j); else - result = str().mk_nth_i(t, m_autil.mk_int(0)); + result = str().mk_nth_c(t, 0); return result; } +expr_ref seq_rewriter::mk_sub(expr* a, rational const& n) { + expr* a1, *a2; + SASSERT(n.is_int()); + rational k; + if (m_autil.is_sub(a, a1, a2) && m_autil.is_numeral(a2, k)) + return expr_ref(m_autil.mk_sub(a1, m_autil.mk_int(k + n)), m()); + if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a2, k)) + return expr_ref(m_autil.mk_add(a1, m_autil.mk_int(k - n)), m()); + if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a1, k)) + return expr_ref(m_autil.mk_add(a2, m_autil.mk_int(k - n)), m()); + return expr_ref(m_autil.mk_sub(a, m_autil.mk_int(n)), m()); +} + + /* * In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1) * This method assumes that |t| > 0. */ expr_ref seq_rewriter::mk_seq_rest(expr* t) { expr_ref result(m()); - expr* s, * j, * k; - expr_ref one(m_autil.mk_int(1), m()); - if (str().is_extract(t, s, j, k)) - result = str().mk_substr(s, m_autil.mk_add(j, one), m_autil.mk_sub(k, one)); - else - result = str().mk_substr(t, one, m_autil.mk_sub(str().mk_length(t), one)); + expr* s, * j, * k; + rational jv; + if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, jv) && jv >= 0) + result = str().mk_substr(s, m_autil.mk_int(jv + 1), mk_sub(k, 1)); + else + result = str().mk_substr(t, one(), mk_sub(str().mk_length(t), 1)); return result; } /* -* In general constructs nth(t,|t|-1) but if t = substring(s,j,k) then simplifies to nth(s,j+k-1) +* In general constructs nth(t,|t|-1) but if t = substring(s,j,|s|-j) j >= 0, then simplifies to nth(s,|s|-1) * This method assumes that |t| > 0. */ expr_ref seq_rewriter::mk_seq_last(expr* t) { expr_ref result(m()); - expr* s, * j, * k; - expr_ref one(m_autil.mk_int(1), m()); - if (str().is_extract(t, s, j, k)) - result = str().mk_nth_i(s, m_autil.mk_sub(m_autil.mk_add(j, k), one)); + expr* s, * j, * k, * s_, * len_s; + rational jv, i; + if (str().is_extract(t, s, j, k) && + m_autil.is_numeral(j, jv) && jv >= 0 && + str().is_len_sub(k, len_s, s_, i) && + s == s_ && jv == i) { + expr_ref lastpos = mk_sub(len_s, 1); + result = str().mk_nth_i(s, lastpos); + } else - result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one)); + result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one())); return result; } @@ -924,11 +942,14 @@ expr_ref seq_rewriter::mk_seq_last(expr* t) { expr_ref seq_rewriter::mk_seq_butlast(expr* t) { expr_ref result(m()); expr* s, * j, * k; - expr_ref one(m_autil.mk_int(1), m()); - if (str().is_extract(t, s, j, k)) - result = str().mk_substr(s, j, m_autil.mk_sub(k, one)); + if (str().is_extract(t, s, j, k)) { + expr_ref_vector k_min_1(m()); + k_min_1.push_back(k); + k_min_1.push_back(minus_one()); + result = str().mk_substr(s, j, m_autil.mk_add_simplify(k_min_1)); + } else - result = str().mk_substr(t, m_autil.mk_int(0), m_autil.mk_sub(str().mk_length(t), one)); + result = str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one())); return result; } @@ -1678,7 +1699,7 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result return BR_DONE; } if (m_autil.is_numeral(c, r) && r.is_neg()) { - result = m_autil.mk_int(-1); + result = minus_one(); return BR_DONE; } @@ -1688,10 +1709,10 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result } if (str().is_empty(b)) { - result = m().mk_ite(m().mk_and(m_autil.mk_le(m_autil.mk_int(0), c), + result = m().mk_ite(m().mk_and(m_autil.mk_le(zero(), c), m_autil.mk_le(c, str().mk_length(a))), c, - m_autil.mk_int(-1)); + minus_one()); return BR_REWRITE2; } @@ -2307,7 +2328,7 @@ br_status seq_rewriter::mk_str_to_code(expr* a, expr_ref& result) { if (s.length() == 1) result = m_autil.mk_int(s[0]); else - result = m_autil.mk_int(-1); + result = minus_one(); return BR_DONE; } return BR_FAILED; @@ -2448,7 +2469,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { result = m_autil.mk_int(ch - '0'); } else { - result = m_autil.mk_int(-1); + result = minus_one(); } return BR_DONE; } @@ -2456,7 +2477,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { expr_ref_vector as(m()); str().get_concat_units(a, as); if (as.empty()) { - result = m_autil.mk_int(-1); + result = minus_one(); return BR_DONE; } if (str().is_unit(as.back())) { @@ -2466,11 +2487,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { expr_ref tail(str().mk_stoi(as.back()), m()); expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m()); expr_ref stoi_head(str().mk_stoi(head), m()); - result = m().mk_ite(m_autil.mk_ge(stoi_head, m_autil.mk_int(0)), + result = m().mk_ite(m_autil.mk_ge(stoi_head, zero()), m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail), - m_autil.mk_int(-1)); + minus_one()); - result = m().mk_ite(m_autil.mk_ge(tail, m_autil.mk_int(0)), + result = m().mk_ite(m_autil.mk_ge(tail, zero()), result, tail); result = m().mk_ite(str().mk_is_empty(head), @@ -2481,7 +2502,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) { result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort()); result = m().mk_ite(str().mk_is_empty(result), - m_autil.mk_int(0), + zero(), str().mk_stoi(result)); return BR_REWRITE_FULL; } @@ -2573,7 +2594,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { } /* - s = head + tail where |head| = 1 + s = [head] + tail where head is the first element of s */ bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) { expr* h = nullptr, *t = nullptr; @@ -2670,10 +2691,10 @@ expr_ref seq_rewriter::re_predicate(expr* cond, sort* seq_sort) { expr_ref seq_rewriter::is_nullable(expr* r) { STRACE("seq_verbose", tout << "is_nullable: " << mk_pp(r, m()) << std::endl;); - expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr), m()); + expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr, nullptr), m()); if (!result) { result = is_nullable_rec(r); - m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, result); + m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, nullptr, result); } STRACE("seq_verbose", tout << "is_nullable result: " << result << std::endl;); @@ -2691,7 +2712,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) { re().is_intersection(r, r1, r2)) { m_br.mk_and(is_nullable(r1), is_nullable(r2), result); } - else if (re().is_union(r, r1, r2)) { + else if (re().is_union(r, r1, r2) || re().is_antimorov_union(r, r1, r2)) { m_br.mk_or(is_nullable(r1), is_nullable(r2), result); } else if (re().is_diff(r, r1, r2)) { @@ -2701,6 +2722,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) { else if (re().is_star(r) || re().is_opt(r) || re().is_full_seq(r) || + re().is_epsilon(r) || (re().is_loop(r, r1, lo) && lo == 0) || (re().is_loop(r, r1, lo, hi) && lo == 0)) { result = m().mk_true(); @@ -2724,7 +2746,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) { result = is_nullable(r1); } else if (m().is_ite(r, cond, r1, r2)) { - result = m().mk_ite(cond, is_nullable(r1), is_nullable(r2)); + m_br.mk_ite(cond, is_nullable(r1), is_nullable(r2), result); } else if (m_util.is_re(r, seq_sort)) { result = is_nullable_symbolic_regex(r, seq_sort); @@ -2881,7 +2903,8 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) { br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) { result = mk_derivative(ele, r); // TBD: we may even declare BR_DONE here and potentially miss some simplifications - return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL; + // return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL; + return BR_DONE; } /* @@ -2976,29 +2999,379 @@ bool seq_rewriter::check_deriv_normal_form(expr* r, int level) { } #endif -/* - Memoized, recursive implementation of the symbolic derivative such that - the result is in normal form. +expr_ref seq_rewriter::mk_derivative(expr* r) { + sort* seq_sort = nullptr, * ele_sort = nullptr; + VERIFY(m_util.is_re(r, seq_sort)); + VERIFY(m_util.is_seq(seq_sort, ele_sort)); + expr_ref v(m().mk_var(0, ele_sort), m()); + return mk_antimirov_deriv(v, r, m().mk_true()); +} - Functions without _rec are memoized wrappers, which call the _rec - version if lookup fails. - - The main logic is in mk_der_op_rec for combining normal forms. -*/ expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) { - STRACE("seq_verbose", tout << "derivative: " << mk_pp(ele, m()) - << "," << mk_pp(r, m()) << std::endl;); - expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, ele, r), m()); + return mk_antimirov_deriv(ele, r, m().mk_true()); +} + +expr_ref seq_rewriter::mk_antimirov_deriv(expr* e, expr* r, expr* path) { + // Ensure references are owned + expr_ref _e(e, m()), _path(path, m()), _r(r, m()); + expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, e, r, path), m()); if (!result) { - result = mk_derivative_rec(ele, r); - m_op_cache.insert(OP_RE_DERIVATIVE, ele, r, result); + mk_antimirov_deriv_rec(e, r, path, result); + m_op_cache.insert(OP_RE_DERIVATIVE, e, r, path, result); + STRACE("seq_regex", tout << "D(" << mk_pp(e, m()) << "," << mk_pp(r, m()) << "," << mk_pp(path, m()) << ")" << std::endl;); + STRACE("seq_regex", tout << "= " << mk_pp(result, m()) << std::endl;); } - STRACE("seq_verbose", tout << "derivative result: " - << mk_pp(result, m()) << std::endl;); - CASSERT("seq_regex", check_deriv_normal_form(r)); return result; } +void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result) { + sort* seq_sort = nullptr, * ele_sort = nullptr; + VERIFY(m_util.is_re(r, seq_sort)); + VERIFY(m_util.is_seq(seq_sort, ele_sort)); + SASSERT(ele_sort == e->get_sort()); + expr* r1 = nullptr, * r2 = nullptr, * c = nullptr; + expr_ref c1(m()); + expr_ref c2(m()); + auto nothing = [&]() { return expr_ref(re().mk_empty(r->get_sort()), m()); }; + auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); }; + auto dotstar = [&]() { return expr_ref(re().mk_full_seq(r->get_sort()), m()); }; + auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(r->get_sort())), m()); }; + unsigned lo = 0, hi = 0; + if (re().is_empty(r) || re().is_epsilon(r)) + // D(e,[]) = D(e,()) = [] + result = nothing(); + else if (re().is_full_seq(r) || re().is_dot_plus(r)) + // D(e,.*) = D(e,.+) = .* + result = dotstar(); + else if (re().is_full_char(r)) + // D(e,.) = () + result = epsilon(); + else if (re().is_to_re(r, r1)) { + expr_ref h(m()); + expr_ref t(m()); + // here r1 is a sequence + if (get_head_tail(r1, h, t)) { + if (eq_char(e, h)) + result = re().mk_to_re(t); + else if (neq_char(e, h)) + result = nothing(); + else + result = re().mk_ite_simplify(m().mk_eq(e, h), re().mk_to_re(t), nothing()); + } + else { + // observe that the precondition |r1|>0 is is implied by c1 for use of mk_seq_first + m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_first(r1), e), c1); + m_br.mk_and(path, c1, c2); + if (m().is_false(c2)) + result = nothing(); + else + // observe that the precondition |r1|>0 is implied by c1 for use of mk_seq_rest + result = m().mk_ite(c1, re().mk_to_re(mk_seq_rest(r1)), nothing()); + } + } + else if (re().is_reverse(r, r2)) + if (re().is_to_re(r2, r1)) { + // here r1 is a sequence + // observe that the precondition |r1|>0 of mk_seq_last is implied by c1 + m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_last(r1), e), c1); + m_br.mk_and(path, c1, c2); + if (m().is_false(c2)) + result = nothing(); + else + // observe that the precondition |r1|>0 of mk_seq_rest is implied by c1 + result = re().mk_ite_simplify(c1, re().mk_reverse(re().mk_to_re(mk_seq_butlast(r1))), nothing()); + } + else { + result = mk_regex_reverse(r2); + if (result.get() == r) + //r2 is an uninterpreted regex that is stuck + //for example if r = (re.reverse R) where R is a regex variable then + //here result.get() == r + result = re().mk_derivative(e, result); + else + result = mk_antimirov_deriv(e, result, path); + } + else if (re().is_concat(r, r1, r2)) { + expr_ref r1nullable(is_nullable(r1), m()); + c1 = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), r2); + expr_ref r1nullable_and_path(m()); + m_br.mk_and(r1nullable, path, r1nullable_and_path); + if (m().is_false(r1nullable_and_path)) + // D(e,r1)r2 + result = c1; + else + // D(e,r1)r2|(ite (r1nullable) (D(e,r2)) []) + // observe that (mk_ite_simplify(true, D(e,r2), []) = D(e,r2) + result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing())); + } + else if (m().is_ite(r, c, r1, r2)) { + c1 = simplify_path(m().mk_and(c, path)); + c2 = simplify_path(m().mk_and(m().mk_not(c), path)); + if (m().is_false(c1)) + result = mk_antimirov_deriv(e, r2, c2); + else if (m().is_false(c2)) + result = mk_antimirov_deriv(e, r1, c1); + else + result = re().mk_ite_simplify(c, mk_antimirov_deriv(e, r1, c1), mk_antimirov_deriv(e, r2, c2)); + } + else if (re().is_range(r, r1, r2)) { + expr_ref range(m()); + expr_ref psi(m()); + if (str().is_unit_string(r1, c1) && str().is_unit_string(r2, c2)) { + SASSERT(u().is_char(c1)); + SASSERT(u().is_char(c2)); + // range represents c1 <= e <= c2 + range = simplify_path(m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2))); + psi = simplify_path(m().mk_and(path, range)); + if (m().is_false(psi)) + result = nothing(); + else + // D(e,c1..c2) = if (c1<=e<=c2) then () else [] + result = re().mk_ite_simplify(range, epsilon(), nothing()); + } + else + result = nothing(); + } + else if (re().is_union(r, r1, r2)) + result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path)); + else if (re().is_intersection(r, r1, r2)) + result = mk_antimirov_deriv_intersection( + mk_antimirov_deriv(e, r1, path), + mk_antimirov_deriv(e, r2, path), m().mk_true()); + else if (re().is_star(r, r1) || re().is_plus(r, r1) || (re().is_loop(r, r1, lo) && 0 <= lo && lo <= 1)) + result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_star(r1)); + else if (re().is_loop(r, r1, lo)) + result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, lo - 1)); + else if (re().is_loop(r, r1, lo, hi)) { + if (lo == 0 && hi == 0 || hi < lo) + result = nothing(); + else + result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, (lo == 0 ? 0 : lo - 1), hi - 1)); + } + else if (re().is_opt(r, r1)) + result = mk_antimirov_deriv(e, r1, path); + else if (re().is_complement(r, r1)) + // D(e,~r1) = ~D(e,r1) + result = mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r1, path)); + else if (re().is_diff(r, r1, r2)) + result = mk_antimirov_deriv_intersection( + mk_antimirov_deriv(e, r1, path), + mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r2, path)), m().mk_true()); + else if (re().is_of_pred(r, r1)) { + array_util array(m()); + expr* args[2] = { r1, e }; + result = array.mk_select(2, args); + // Use mk_der_cond to normalize + result = mk_der_cond(result, e, seq_sort); + } + else + // stuck cases + result = re().mk_derivative(e, r); +} + +expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path) { + sort* seq_sort = nullptr, * ele_sort = nullptr; + VERIFY(m_util.is_re(d1, seq_sort)); + VERIFY(m_util.is_seq(seq_sort, ele_sort)); + expr_ref result(m()); + expr* c, * a, * b; + if (d1 == d2 || re().is_full_seq(d2) || re().is_empty(d1)) + result = d1; + else if (re().is_full_seq(d1) || re().is_empty(d2)) + result = d2; + else if (m().is_ite(d1, c, a, b)) { + expr_ref path_and_c(simplify_path(m().mk_and(path, c)), m()); + expr_ref path_and_notc(simplify_path(m().mk_and(path, m().mk_not(c))), m()); + if (m().is_false(path_and_c)) + result = mk_antimirov_deriv_intersection(b, d2, path); + else if (m().is_false(path_and_notc)) + result = mk_antimirov_deriv_intersection(a, d2, path); + else + result = m().mk_ite(c, mk_antimirov_deriv_intersection(a, d2, path_and_c), + mk_antimirov_deriv_intersection(b, d2, path_and_notc)); + } + else if (m().is_ite(d2)) + // swap d1 and d2 + result = mk_antimirov_deriv_intersection(d2, d1, path); + else if (re().is_union(d1, a, b)) + // distribute intersection over the union in d1 + result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(a, d2, path), mk_antimirov_deriv_intersection(b, d2, path)); + else if (re().is_union(d2, a, b)) + // distribute intersection over the union in d2 + result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(d1, a, path), mk_antimirov_deriv_intersection(d1, b, path)); + else + // in all other cases create the intersection regex + // TODO: flatten, order and merge d1 and d2 to maintain equality under similarity + result = (d1->get_id() <= d2->get_id() ? re().mk_inter(d1, d2) : re().mk_inter(d2, d1)); + return result; +} + +expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) { + expr_ref result(m()); + // Take reference count of r and d + expr_ref _r(r, m()), _d(d, m()); + expr* c, * t, * e; + if (m().is_ite(d, c, t, e)) + result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r)); + else if (re().is_union(d, t, e)) + result = re().mk_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r)); + else + result = mk_re_append(d, r); + return result; +} + +expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* d) { + sort* seq_sort = nullptr, * ele_sort = nullptr; + VERIFY(m_util.is_re(d, seq_sort)); + auto nothing = [&]() { return expr_ref(re().mk_empty(d->get_sort()), m()); }; + auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); }; + auto dotstar = [&]() { return expr_ref(re().mk_full_seq(d->get_sort()), m()); }; + auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(d->get_sort())), m()); }; + expr_ref result(m()); + expr* c, * t, * e; + if (re().is_empty(d)) + result = dotstar(); + else if (re().is_epsilon(d)) + result = dotplus(); + else if (re().is_full_seq(d)) + result = nothing(); + else if (re().is_dot_plus(d)) + result = epsilon(); + else if (m().is_ite(d, c, t, e)) + result = m().mk_ite(c, mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e)); + else if (re().is_union(d, t, e)) + result = re().mk_inter(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e)); + else if (re().is_intersection(d, t, e)) + result = re().mk_union(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e)); + else if (re().is_complement(d, t)) + result = t; + else + result = re().mk_complement(d); + return result; +} + +expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) { + expr_ref result(m()); + if (re().is_empty(d1) || re().is_full_seq(d2)) + result = d2; + else if (re().is_empty(d2) || re().is_full_seq(d1)) + result = d1; + else if (re().is_dot_plus(d1) && re().get_info(d2).min_length > 0) + result = d1; + else if (re().is_dot_plus(d2) && re().get_info(d1).min_length > 0) + result = d2; + else + // TODO: flatten, order and merge d1 and d2 to maintain equality under similarity + result = (d1->get_id() <= d2->get_id() ? re().mk_union(d1, d2) : re().mk_union(d2, d1)); + return result; +} + +expr_ref seq_rewriter::mk_regex_reverse(expr* r) { + expr* r1 = nullptr, * r2 = nullptr, * c = nullptr; + unsigned lo = 0, hi = 0; + expr_ref result(m()); + if (re().is_empty(r) || re().is_range(r) || re().is_epsilon(r) || re().is_full_seq(r) || + re().is_full_char(r) || re().is_dot_plus(r) || re().is_of_pred(r)) + result = r; + else if (re().is_to_re(r)) + result = re().mk_reverse(r); + else if (re().is_reverse(r, r1)) + result = r1; + else if (re().is_concat(r, r1, r2)) + result = mk_regex_concat(mk_regex_reverse(r2), mk_regex_reverse(r1)); + else if (m().is_ite(r, c, r1, r2)) + result = m().mk_ite(c, mk_regex_reverse(r1), mk_regex_reverse(r2)); + else if (re().is_union(r, r1, r2)) + result = re().mk_union(mk_regex_reverse(r1), mk_regex_reverse(r2)); + else if (re().is_intersection(r, r1, r2)) + result = re().mk_inter(mk_regex_reverse(r1), mk_regex_reverse(r2)); + else if (re().is_diff(r, r1, r2)) + result = re().mk_diff(mk_regex_reverse(r1), mk_regex_reverse(r2)); + else if (re().is_star(r, r1)) + result = re().mk_star(mk_regex_reverse(r1)); + else if (re().is_plus(r, r1)) + result = re().mk_plus(mk_regex_reverse(r1)); + else if (re().is_loop(r, r1, lo)) + result = re().mk_loop(mk_regex_reverse(r1), lo); + else if (re().is_loop(r, r1, lo, hi)) + result = re().mk_loop(mk_regex_reverse(r1), lo, hi); + else if (re().is_opt(r, r1)) + result = re().mk_opt(mk_regex_reverse(r1)); + else if (re().is_complement(r, r1)) + result = re().mk_complement(mk_regex_reverse(r1)); + else + //stuck cases: such as r being a regex variable + //observe that re().mk_reverse(to_re(s)) is not a stuck case + result = re().mk_reverse(r); + return result; +} + +expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) { + sort* seq_sort = nullptr; + VERIFY(m_util.is_re(r, seq_sort)); + SASSERT(r->get_sort() == s->get_sort()); + expr_ref result(m()); + expr* r1, * r2; + if (re().is_epsilon(r) || re().is_empty(s)) + result = s; + else if (re().is_epsilon(s) || re().is_empty(r)) + result = r; + else if (re().is_full_seq(r) && re().is_full_seq(s)) + result = r; + else if (re().is_concat(r, r1, r2)) + //create the resulting concatenation in right-associative form + result = mk_regex_concat(r1, mk_regex_concat(r2, s)); + else { + //TODO: perhaps simplifiy some further cases such as .*. = ..* = .*.+ = .+.* = .+ + result = re().mk_concat(r, s); + } + return result; +} + +expr_ref seq_rewriter::mk_in_antimirov(expr* s, expr* d){ + expr_ref result(mk_in_antimirov_rec(s, d), m()); + return result; +} + +expr_ref seq_rewriter::mk_in_antimirov_rec(expr* s, expr* d) { + expr* c, * d1, * d2; + expr_ref result(m()); + if (re().is_full_seq(d) || (str().min_length(s) > 0 && re().is_dot_plus(d))) + // s in .* <==> true, also: s in .+ <==> true when |s|>0 + result = m().mk_true(); + else if (re().is_empty(d) || (str().min_length(s) > 0 && re().is_epsilon(d))) + // s in [] <==> false, also: s in () <==> false when |s|>0 + result = m().mk_false(); + else if (m().is_ite(d, c, d1, d2)) + result = re().mk_ite_simplify(c, mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2)); + else if (re().is_union(d, d1, d2)) + m_br.mk_or(mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2), result); + else + result = re().mk_in_re(s, d); + return result; +} + +/* +path is typically a conjunction of (negated) character equations or constraints that can potentially be simplified +the first element of each equation is assumed to be the element parameter, for example x = (:var 0), +for example a constraint x='a' & x='b' is simplified to false +*/ +expr_ref seq_rewriter::simplify_path(expr* path) { + //TODO: more systematic simplifications + expr_ref result(path, m()); + expr* h = nullptr, * t = nullptr, * lhs = nullptr, * rhs = nullptr, * h1 = nullptr; + if (m().is_and(path, h, t)) { + if (m().is_true(h)) + result = simplify_path(t); + else if (m().is_true(t)) + result = simplify_path(h); + else if (m().is_eq(h, lhs, rhs) || m().is_not(h, h1) && m().is_eq(h1, lhs, rhs)) + elim_condition(lhs, result); + } + return result; +} + + expr_ref seq_rewriter::mk_der_antimorov_union(expr* r1, expr* r2) { return mk_der_op(_OP_RE_ANTIMOROV_UNION, r1, r2); } @@ -3016,7 +3389,7 @@ expr_ref seq_rewriter::mk_der_concat(expr* r1, expr* r2) { } /* - Utility functions to decide char <, ==, and <=. + Utility functions to decide char <, ==, !=, and <=. Return true if deduced, false if unknown. */ bool seq_rewriter::lt_char(expr* ch1, expr* ch2) { @@ -3027,6 +3400,11 @@ bool seq_rewriter::lt_char(expr* ch1, expr* ch2) { bool seq_rewriter::eq_char(expr* ch1, expr* ch2) { return ch1 == ch2; } +bool seq_rewriter::neq_char(expr* ch1, expr* ch2) { + unsigned u1, u2; + return u().is_const_char(ch1, u1) && + u().is_const_char(ch2, u2) && (u1 != u2); +} bool seq_rewriter::le_char(expr* ch1, expr* ch2) { return eq_char(ch1, ch2) || lt_char(ch1, ch2); } @@ -3257,10 +3635,10 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) { default: break; } - result = m_op_cache.find(k, a, b); + result = m_op_cache.find(k, a, b, nullptr); if (!result) { result = mk_der_op_rec(k, a, b); - m_op_cache.insert(k, a, b, result); + m_op_cache.insert(k, a, b, nullptr, result); } CASSERT("seq_regex", check_deriv_normal_form(result)); return result; @@ -3269,7 +3647,7 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) { expr_ref seq_rewriter::mk_der_compl(expr* r) { STRACE("seq_verbose", tout << "mk_der_compl: " << mk_pp(r, m()) << std::endl;); - expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr), m()); + expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr, nullptr), m()); if (!result) { expr* c = nullptr, * r1 = nullptr, * r2 = nullptr; if (re().is_antimorov_union(r, r1, r2)) { @@ -3285,7 +3663,7 @@ expr_ref seq_rewriter::mk_der_compl(expr* r) { } else if (BR_FAILED == mk_re_complement(r, result)) result = re().mk_complement(r); - m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, result); + m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, nullptr, result); } CASSERT("seq_regex", check_deriv_normal_form(result)); return result; @@ -3509,7 +3887,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) { // construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else [])) // hd = first of reverse(r2) i.e. last of r2 // tl = rest of reverse(r2) i.e. butlast of r2 - //hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), m_autil.mk_int(1))); + //hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), one())); hd = mk_seq_last(r2); m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result); tl = re().mk_to_re(mk_seq_butlast(r2)); @@ -3537,9 +3915,9 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) { return mk_empty(); } } - expr* e1 = nullptr, *e2 = nullptr; + expr* e1 = nullptr, * e2 = nullptr; if (str().is_unit(r1, e1) && str().is_unit(r2, e2)) { - SASSERT(u().is_char(e1)); + SASSERT(u().is_char(e1)); // Use mk_der_cond to normalize STRACE("seq_verbose", tout << "deriv range str" << std::endl;); expr_ref p1(u().mk_le(e1, ele), m()); @@ -3760,7 +4138,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { (re().is_union(b, b1, eps) && re().is_epsilon(eps)) || (re().is_union(b, eps, b1) && re().is_epsilon(eps))) { - result = m().mk_ite(m().mk_eq(str().mk_length(a), m_autil.mk_int(0)), + result = m().mk_ite(m().mk_eq(str().mk_length(a), zero()), m().mk_true(), re().mk_in_re(a, b1)); return BR_REWRITE_FULL; @@ -3775,8 +4153,10 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { expr_ref hd(m()), tl(m()); if (get_head_tail(a, hd, tl)) { - result = re().mk_in_re(tl, re().mk_derivative(hd, b)); - return BR_REWRITE2; + //result = re().mk_in_re(tl, re().mk_derivative(hd, b)); + //result = re().mk_in_re(tl, mk_derivative(hd, b)); + result = mk_in_antimirov(tl, mk_antimirov_deriv(hd, b, m().mk_true())); + return BR_REWRITE_FULL; } if (get_head_tail_reversed(a, hd, tl)) { @@ -3791,7 +4171,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { expr_ref len_a(str().mk_length(a), m()); expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m()); result = m().mk_and(m_autil.mk_ge(len_a, len_hd), - re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd), + re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd), re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)); return BR_REWRITE_FULL; } @@ -3802,7 +4182,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m()); expr* s = nullptr; result = m().mk_and(m_autil.mk_ge(len_a, len_tl), - re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd), + re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd), (re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) : re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl))); return BR_REWRITE_FULL; @@ -3912,6 +4292,10 @@ br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) { return BR_REWRITE2; } expr* a1 = nullptr, *b1 = nullptr; + if (re().is_to_re(a, a1) && re().is_to_re(b, b1)) { + result = re().mk_to_re(str().mk_concat(a1, b1)); + return BR_DONE; + } if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) { result = a; return BR_DONE; @@ -5151,15 +5535,15 @@ bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) { if (str().is_extract(r, s, offset, len)) { expr_ref len_s(str().mk_length(s), m()); expr_ref_vector fmls(m()); - fmls.push_back(m_autil.mk_lt(offset, m_autil.mk_int(0))); + fmls.push_back(m_autil.mk_lt(offset, zero())); fmls.push_back(m().mk_eq(s, l)); - fmls.push_back(m_autil.mk_le(len, m_autil.mk_int(0))); + fmls.push_back(m_autil.mk_le(len, zero())); fmls.push_back(m_autil.mk_le(len_s, offset)); result = m().mk_or(fmls); return true; } if (str().is_itos(r, s)) { - result = m_autil.mk_lt(s, m_autil.mk_int(0)); + result = m_autil.mk_lt(s, zero()); return true; } return false; @@ -5275,19 +5659,20 @@ seq_rewriter::op_cache::op_cache(ast_manager& m): m_trail(m) {} -expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b) { - op_entry e(op, a, b, nullptr); +expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b, expr* c) { + op_entry e(op, a, b, c, nullptr); m_table.find(e, e); return e.r; } -void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* r) { +void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* c, expr* r) { cleanup(); if (a) m_trail.push_back(a); if (b) m_trail.push_back(b); + if (c) m_trail.push_back(c); if (r) m_trail.push_back(r); - m_table.insert(op_entry(op, a, b, r)); + m_table.insert(op_entry(op, a, b, c, r)); } void seq_rewriter::op_cache::cleanup() { diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index f726a4673..20978c279 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -117,20 +117,20 @@ class seq_rewriter { class op_cache { struct op_entry { decl_kind k; - expr* a, *b, *r; - op_entry(decl_kind k, expr* a, expr* b, expr* r): k(k), a(a), b(b), r(r) {} - op_entry():k(0), a(nullptr), b(nullptr), r(nullptr) {} + expr* a, *b, *c, *r; + op_entry(decl_kind k, expr* a, expr* b, expr* c, expr* r): k(k), a(a), b(b), c(c), r(r) {} + op_entry():k(0), a(nullptr), b(nullptr), c(nullptr), r(nullptr) {} }; struct hash_entry { unsigned operator()(op_entry const& e) const { - return mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0); + return combine_hash(mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0), e.c ? e.c->get_id() : 0); } }; struct eq_entry { - bool operator()(op_entry const& a, op_entry const& b) const { - return a.k == b.k && a.a == b.a && a.b == b.b; + bool operator()(op_entry const& a, op_entry const& b) const { + return a.k == b.k && a.a == b.a && a.b == b.b && a.c == b.c; } }; @@ -143,8 +143,8 @@ class seq_rewriter { public: op_cache(ast_manager& m); - expr* find(decl_kind op, expr* a, expr* b); - void insert(decl_kind op, expr* a, expr* b, expr* r); + expr* find(decl_kind op, expr* a, expr* b, expr* c); + void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r); }; seq_util m_util; @@ -208,8 +208,24 @@ class seq_rewriter { bool check_deriv_normal_form(expr* r, int level = 3); #endif + void mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result); + + expr_ref mk_antimirov_deriv(expr* e, expr* r, expr* path); + expr_ref mk_in_antimirov_rec(expr* s, expr* d); + expr_ref mk_in_antimirov(expr* s, expr* d); + + expr_ref mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path); + expr_ref mk_antimirov_deriv_concat(expr* d, expr* r); + expr_ref mk_antimirov_deriv_negate(expr* d); + expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2); + expr_ref mk_regex_reverse(expr* r); + expr_ref mk_regex_concat(expr* r1, expr* r2); + + expr_ref simplify_path(expr* path); + bool lt_char(expr* ch1, expr* ch2); bool eq_char(expr* ch1, expr* ch2); + bool neq_char(expr* ch1, expr* ch2); bool le_char(expr* ch1, expr* ch2); bool pred_implies(expr* a, expr* b); bool are_complements(expr* r1, expr* r2) const; @@ -286,6 +302,8 @@ class seq_rewriter { expr_ref zero() { return expr_ref(m_autil.mk_int(0), m()); } expr_ref one() { return expr_ref(m_autil.mk_int(1), m()); } expr_ref minus_one() { return expr_ref(m_autil.mk_int(-1), m()); } + expr_ref mk_sub(expr* a, rational const& n); + expr_ref mk_sub(expr* a, unsigned n) { return mk_sub(a, rational(n)); } bool is_suffix(expr* s, expr* offset, expr* len); bool is_prefix(expr* s, expr* offset, expr* len); @@ -379,9 +397,19 @@ public: void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& new_eqs); - // Expose derivative and nullability check + /* + create the nullability check for r + */ expr_ref is_nullable(expr* r); + /* + make the derivative of r wrt the given element ele + */ expr_ref mk_derivative(expr* ele, expr* r); + /* + make the derivative of r wrt the canonical variable v0 = (:var 0), + for example mk_derivative(a+) = (if (v0 = 'a') then a* else []) + */ + expr_ref mk_derivative(expr* r); // heuristic elimination of element from condition that comes form a derivative. // special case optimization for conjunctions of equalities, disequalities and ranges. diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 69ec244d5..cc78da8f6 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -839,7 +839,7 @@ bool seq_util::str::is_nth_i(expr const* n, expr*& s, unsigned& idx) const { return arith_util(m).is_unsigned(i, idx); } -app* seq_util::str::mk_nth_i(expr* s, unsigned i) const { +app* seq_util::str::mk_nth_c(expr* s, unsigned i) const { return mk_nth_i(s, arith_util(m).mk_int(i)); } @@ -854,6 +854,48 @@ void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const { } } +/* +Returns true if s is an expression of the form (l = |u|) |u|-k or (-k)+|u| or |u|+(-k). +Also returns true and assigns k=0 and l=s if s is |u|. +*/ +bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) const { + expr* x; + rational v; + arith_util a(m); + if (is_length(s, l)) { + k = 0; + return true; + } + else if (a.is_sub(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonneg()) { + k = v; + return true; + } + else if (a.is_add(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) { + k = - v; + return true; + } + else if (a.is_add(s, x, l) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) { + k = - v; + return true; + } + else + return false; +} + +bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const { + zstring z; + expr* ch = nullptr; + if (is_string(s, z) && z.length() == 1) { + c = mk_char(z[0]); + return true; + } + else if (is_unit(s, ch)) { + c = ch; + return true; + } + return false; +} + void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const { expr* e1, *e2; while (is_concat(e, e1, e2)) { @@ -876,8 +918,6 @@ app* seq_util::str::mk_is_empty(expr* s) const { return m.mk_eq(s, mk_empty(s->get_sort())); } - - unsigned seq_util::str::min_length(expr* s) const { SASSERT(u.is_seq(s)); unsigned result = 0; @@ -1065,38 +1105,71 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) { /* Produces compact view of concrete concatenations such as (abcd). */ -std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const { +std::ostream& seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const { SASSERT(re.u.is_seq(s)); zstring z; + expr* x, * j, * k, * l, * i, * x_; if (re.u.str.is_empty(s)) out << "()"; else if (re.u.str.is_unit(s)) - seq_unit(out, s); + print_unit(out, s); else if (re.u.str.is_concat(s)) { expr_ref_vector es(re.m); re.u.str.get_concat(s, es); for (expr* e : es) - compact_helper_seq(out, e); + print_seq(out, e); } else if (re.u.str.is_string(s, z)) { for (unsigned i = 0; i < z.length(); i++) out << (char)z[i]; } - //using braces to indicate 'full' output - //for example an uninterpreted constant X will be printed as {X} - //while a unit sequence "X" will be printed as X - //thus for example (concat "X" "Y" Z "W") where Z is uninterpreted is printed as XY{Z}W - else out << "{" << mk_pp(s, re.m) << "}"; + else if (re.u.str.is_extract(s, x, j, k)) { + rational jv, iv; + print(out, x); + if (arith_util(re.m).is_numeral(j, jv)) { + if (arith_util(re.m).is_numeral(k, iv)) { + // output X[j,k] + out << "[" << jv.get_int32() << "," << jv.get_int32() << "]"; + } + else if (arith_util(re.m).is_sub(k, l, i) && re.u.str.is_length(l, x_) && x == x_ && + arith_util(re.m).is_numeral(i, iv) && iv == jv) { + // case X[j,|X|-j] is denoted by X[j..] + out << "[" << jv.get_int32() << "..]"; + } + else if (((arith_util(re.m).is_add(k, l, i) && re.u.str.is_length(l, x_)) || + (arith_util(re.m).is_add(k, i, l) && re.u.str.is_length(l, x_))) && x == x_ && + arith_util(re.m).is_numeral(i, iv) && iv.get_int32() + jv.get_int32() == 0) { + // case X[j,|X|-j] is denoted by X[j..] + out << "[" << jv.get_int32() << "..]"; + } + else { + out << "[" << jv.get_int32() << ","; + print(out, k); + out << "]"; + } + } + else { + out << "["; + print(out, j); + out << ","; + print(out, k); + out << "]"; + } + } + else + out << mk_pp(s, re.m); return out; } /* Produces output such as [a-z] for a range. */ -std::ostream& seq_util::rex::pp::compact_helper_range(std::ostream& out, expr* s1, expr* s2) const { +std::ostream& seq_util::rex::pp::print_range(std::ostream& out, expr* s1, expr* s2) const { out << "["; - seq_unit(out, s1) << "-"; - seq_unit(out, s2) << "]"; + print_unit(out, s1); + out << "-"; + print_unit(out, s2); + out << "]"; return out; } @@ -1111,8 +1184,8 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const { /* Specialize output for a unit sequence converting to visible ASCII characters if possible. */ -std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const { - expr* e; +std::ostream& seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const { + expr* e, * i; unsigned n = 0; if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) { char c = (char)n; @@ -1122,22 +1195,21 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const { out << "\\r"; else if (c == '\f') out << "\\f"; - else if (c == ' ') - out << "\\s"; - else if (c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '.' || c == '\\') - out << "\\" << c; - else if (32 < n && n < 127) { + else if (32 <= n && n < 127 && n != '\"' && n != ' ' + && n != '\\' && n != '\'' && n != '?' && n != '.' && n != '(' && n != ')' && n != '[' && n != ']' + && n != '{' && n != '}' && n != '&') { if (html_encode) { if (c == '<') out << "<"; else if (c == '>') out << ">"; - else if (c == '&') - out << "&"; - else if (c == '\"') - out << """; + //else if (c == '&') + // out << "&"; + //else if (c == '\"') + // out << """; else - out << "\\x" << std::hex << n; + //out << "\\x" << std::hex << n; + out << c; } else out << c; @@ -1151,92 +1223,188 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const { else out << "\\u" << std::hex << n; } + else if (re.u.str.is_nth_i(s, e, i)) { + print(out, e); + out << "[" << mk_pp(i, re.m) << "]"; + } + else if (re.m.is_value(e)) + out << mk_pp(e, re.m); + else if (is_app(e)) { + out << "(" << to_app(e)->get_decl()->get_name().str(); + for (expr * arg : *to_app(e)) + print(out << " ", arg); + out << ")"; + } else - out << "{" << mk_pp(s, re.m) << "}"; + out << mk_pp(s, re.m); return out; } /* - Pretty prints the regex r into the out stream + Pretty prints the regex r into the ostream out */ -std::ostream& seq_util::rex::pp::display(std::ostream& out) const { +std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr; unsigned lo = 0, hi = 0; + rational v; if (re.u.is_char(e)) - return seq_unit(out, e); + print_unit(out, e); else if (re.u.is_seq(e)) - return compact_helper_seq(out, e); + print_seq(out, e); else if (re.is_full_char(e)) - return out << "."; + out << "."; else if (re.is_full_seq(e)) - return out << ".*"; + out << ".*"; else if (re.is_to_re(e, s)) - return compact_helper_seq(out, s); - else if (re.is_range(e, s, s2)) - return compact_helper_range(out, s, s2); + print_seq(out, s); + else if (re.is_range(e, s, s2)) + print_range(out, s, s2); else if (re.is_epsilon(e)) - return out << "()"; + // ε = epsilon + out << (html_encode ? "ε" : "()"); else if (re.is_empty(e)) - return out << "[]"; - else if (re.is_concat(e, r1, r2)) - return out << pp(re, r1) << pp(re, r2); - else if (re.is_union(e, r1, r2)) - return out << "(" << pp(re, r1) << "|" << pp(re, r2) << ")"; - else if (re.is_intersection(e, r1, r2)) - return out << "(" << pp(re, r1) << "&" /*(html_encode ? ")&(" : ")&(")*/ << pp(re, r2) << ")"; + // ∅ = emptyset + out << (html_encode ? "∅" : "[]"); + else if (re.is_concat(e, r1, r2)) { + print(out, r1); + print(out, r2); + } + else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) { + out << "("; + print(out, r1); + out << (html_encode ? "⋃" : "|"); + print(out, r2); + out << ")"; + } + else if (re.is_intersection(e, r1, r2)) + { + out << "("; + print(out, r1); + out << (html_encode ? "⋂" : "&"); + print(out, r2); + out << ")"; + } else if (re.is_complement(e, r1)) { + out << "~"; if (can_skip_parenth(r1)) - return out << "~" << pp(re, r1); - else - return out << "~(" << pp(re, r1) << ")"; + print(out, r1); + else { + out << "("; + print(out, r1); + out << ")"; + } } else if (re.is_plus(e, r1)) { - if (can_skip_parenth(r1)) - return out << pp(re, r1) << "+"; - else - return out << "(" << pp(re, r1) << ")+"; + if (can_skip_parenth(r1)) { + print(out, r1); + out << "+"; + } + else { + out << "("; + print(out, r1); + out << ")+"; + } } else if (re.is_star(e, r1)) { - if (can_skip_parenth(r1)) - return out << pp(re, r1) << "*"; - else - return out << "(" << pp(re, r1) << ")*"; + if (can_skip_parenth(r1)) { + print(out, r1); + out << "*"; + } + else { + out << "("; + print(out, r1); + out << ")*"; + } } else if (re.is_loop(e, r1, lo)) { - if (can_skip_parenth(r1)) - return out << pp(re, r1) << "{" << lo << ",}"; - else - return out << "(" << pp(re, r1) << "){" << lo << ",}"; + if (can_skip_parenth(r1)) { + print(out, r1); + out << "{" << lo << ",}"; + } + else + { + out << "("; + print(out, r1); + out << "){" << lo << ",}"; + } } else if (re.is_loop(e, r1, lo, hi)) { if (can_skip_parenth(r1)) { + print(out, r1); if (lo == hi) - return out << pp(re, r1) << "{" << lo << "}"; - else - return out << pp(re, r1) << "{" << lo << "," << hi << "}"; + out << "{" << lo << "}"; + else + out << "{" << lo << "," << hi << "}"; } else { + out << "("; + print(out, r1); if (lo == hi) - return out << "(" << pp(re, r1) << "){" << lo << "}"; + out << "){" << lo << "}"; else - return out << "(" << pp(re, r1) << "){" << lo << "," << hi << "}"; + out << "){" << lo << "," << hi << "}"; } } - else if (re.is_diff(e, r1, r2)) - return out << "(" << pp(re, r1) << ")\\(" << pp(re, r2) << ")"; - else if (re.m.is_ite(e, s, r1, r2)) - return out << "if(" << mk_pp(s, re.m) << "," << pp(re, r1) << "," << pp(re, r2) << ")"; - else if (re.is_opt(e, r1)) { - if (can_skip_parenth(r1)) - return out << pp(re, r1) << "?"; - else - return out << "(" << pp(re, r1) << ")?"; + else if (re.is_diff(e, r1, r2)) { + out << "("; + print(out, r1); + out << ")\\("; + print(out, r2); + out << ")"; + } + else if (re.m.is_ite(e, s, r1, r2)) { + out << (html_encode ? "(𝐢𝐟 " : "(if "); + print(out, s); + out << (html_encode ? " 𝐭𝗵𝐞𝐧 " : " then "); + print(out, r1); + out << (html_encode ? " 𝐞𝐥𝘀𝐞 " : " else "); + print(out, r2); + out << ")"; + } + else if (re.is_opt(e, r1)) { + if (can_skip_parenth(r1)) { + print(out, r1); + out << "?"; + } + else { + out << "("; + print(out, r1); + out << ")?"; + } + } + else if (re.is_reverse(e, r1)) { + out << "(reverse "; + print(out, r1); + out << ")"; + } + else if (re.m.is_eq(e, r1, r2)) { + out << "("; + print(out, r1); + out << "="; + print(out, r2); + out << ")"; + } + else if (re.m.is_not(e, r1)) { + out << "!"; + print(out, r1); + } + else if (re.m.is_value(e)) + out << mk_pp(e, re.m); + else if (is_app(e)) { + out << "(" << to_app(e)->get_decl()->get_name().str(); + for (expr* arg : *to_app(e)) + print(out << " ", arg); + out << ")"; } - else if (re.is_reverse(e, r1)) - return out << "reverse(" << pp(re, r1) << ")"; else - // Else: derivative or is_of_pred - return out << "{" << mk_pp(e, re.m) << "}"; + // for all remaining cases use the default pretty printer + out << mk_pp(e, re.m); + return out; +} + +std::ostream& seq_util::rex::pp::display(std::ostream& out) const { + print(out, ex); + return out; } /* @@ -1244,7 +1412,16 @@ std::ostream& seq_util::rex::pp::display(std::ostream& out) const { */ std::string seq_util::rex::to_str(expr* r) const { std::ostringstream out; - out << pp(u.re, r); + pp(u.re, r, false).display(out); + return out.str(); +} + +/* + Pretty prints the regex r into the output string that is htmlencoded +*/ +std::string seq_util::rex::to_strh(expr* r) const { + std::ostringstream out; + pp(u.re, r, true).display(out); return out.str(); } @@ -1290,7 +1467,7 @@ seq_util::rex::info seq_util::rex::get_info_rec(expr* e) const { else result = mk_info_rec(to_app(e)); m_infos.setx(e->get_id(), result, invalid_info); - STRACE("re_info", tout << "compute_info(" << pp(u.re, e) << ")=" << result << std::endl;); + STRACE("re_info", tout << "compute_info(" << pp(u.re, e, false) << ")=" << result << std::endl;); return result; } diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index f9589fee6..6bffbf62e 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -286,7 +286,7 @@ public: app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); } app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); } app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); } - app* mk_nth_i(expr* s, unsigned i) const; + app* mk_nth_c(expr* s, unsigned i) const; app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); } app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); } @@ -350,6 +350,13 @@ public: bool is_from_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_FROM_CODE); } bool is_to_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_TO_CODE); } + bool is_len_sub(expr const* n, expr*& l, expr*& u, rational& k) const; + + /* + tests if s is a single character string(c) or a unit (c) + */ + bool is_unit_string(expr const* s, expr_ref& c) const; + bool is_string_term(expr const * n) const { return u.is_string(n->get_sort()); } @@ -530,7 +537,20 @@ public: bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); } bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); } bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); } - bool is_full_seq(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); } + bool is_full_seq(expr const* n) const { + expr* s; + return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET) || (is_star(n, s) && is_full_char(s)); + } + bool is_dot_plus(expr const* n) const { + expr* s, * t; + if (is_plus(n, s) && is_full_char(s)) + return true; + if (is_concat(n, s, t)) { + if ((is_full_char(s) && is_full_seq(t)) || (is_full_char(t) && is_full_seq(s))) + return true; + } + return false; + } bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); } bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); } bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); } @@ -559,18 +579,32 @@ public: app* mk_epsilon(sort* seq_sort); info get_info(expr* r) const; std::string to_str(expr* r) const; + std::string to_strh(expr* r) const; + + expr_ref mk_ite_simplify(expr* c, expr* t, expr* e) + { + expr_ref result(m); + if (m.is_true(c) || t == e) + result = t; + else if (m.is_false(c)) + result = e; + else + result = m.mk_ite(c, t, e); + return result; + } class pp { seq_util::rex& re; - expr* e; + expr* ex; bool html_encode; bool can_skip_parenth(expr* r) const; - std::ostream& seq_unit(std::ostream& out, expr* s) const; - std::ostream& compact_helper_seq(std::ostream& out, expr* s) const; - std::ostream& compact_helper_range(std::ostream& out, expr* s1, expr* s2) const; + std::ostream& print_unit(std::ostream& out, expr* s) const; + std::ostream& print_seq(std::ostream& out, expr* s) const; + std::ostream& print_range(std::ostream& out, expr* s1, expr* s2) const; + std::ostream& print(std::ostream& out, expr* e) const; public: - pp(seq_util::rex& r, expr* e, bool html = false) : re(r), e(e), html_encode(html) {} + pp(seq_util::rex& re, expr* ex, bool html) : re(re), ex(ex), html_encode(html) {} std::ostream& display(std::ostream&) const; }; }; diff --git a/src/smt/seq_regex.cpp b/src/smt/seq_regex.cpp index 80282c3f8..aa1faa131 100644 --- a/src/smt/seq_regex.cpp +++ b/src/smt/seq_regex.cpp @@ -12,6 +12,7 @@ Abstract: Author: Nikolaj Bjorner (nbjorner) 2020-5-22 + Margus Veanes 2021 --*/ @@ -253,10 +254,8 @@ namespace smt { * (accept s (i + 1) (derivative s[i] r) * * Acceptance of a derivative is unfolded into a disjunction over - * all derivatives. Effectively, this implements the following rule, - * but all in one step: - * (accept s i (ite c r1 r2)) => - * c & (accept s i r1) \/ ~c & (accept s i r2) + * all derivatives. Effectively, this implements the following rule: + * (accept s i (ite c r1 r2)) => (ite c (accept s i r1) (accept s i r2)) */ void seq_regex::propagate_accept(literal lit) { SASSERT(!lit.sign()); @@ -302,6 +301,7 @@ namespace smt { unsigned min_len = re().min_length(r); unsigned min_len_plus_i = u().max_plus(min_len, idx); literal len_s_ge_min = th.m_ax.mk_ge(th.mk_len(s), min_len_plus_i); + // Acc(s,i,r) ==> |s| >= i + minlength(r) th.propagate_lit(nullptr, 1, &lit, len_s_ge_min); // Axiom equivalent to the above: th.add_axiom(~lit, len_s_ge_min); @@ -316,6 +316,8 @@ namespace smt { STRACE("seq_regex_brief", tout << " (Warning: min_length returned 0 for" << " non-nullable regex)";); + // since nullable(r) = false: + // Acc(s,i,r) ==> |s|>i th.propagate_lit(nullptr, 1, &lit, ~len_s_le_i); } else if (!m.is_true(is_nullable)) { @@ -327,7 +329,9 @@ namespace smt { << " (Warning: is_nullable did not simplify)";); literal is_nullable_lit = th.mk_literal(is_nullable); ctx.mark_as_relevant(is_nullable_lit); + // Acc(s,i,r) & |s|<=i ==> nullable(r) th.add_axiom(~lit, ~len_s_le_i, is_nullable_lit); + //TODO: what if is_nullable contains an in_re if (str().is_in_re(is_nullable)) th.add_unhandled_expr(is_nullable); } @@ -335,14 +339,19 @@ namespace smt { // Rule 3: derivative unfolding literal_vector accept_next; - expr_ref hd = th.mk_nth(s, i); + expr_ref s_i = th.mk_nth(s, i); expr_ref deriv(m); - deriv = derivative_wrapper(hd, r); + deriv = mk_derivative_wrapper(s_i, r); + STRACE("seq_regex", tout + << "mk_derivative_wrapper: " << re().to_str(deriv) << std::endl;); expr_ref accept_deriv(m); accept_deriv = mk_deriv_accept(s, idx + 1, deriv); accept_next.push_back(~lit); accept_next.push_back(len_s_le_i); accept_next.push_back(th.mk_literal(accept_deriv)); + // Acc(s, i, r) => (|s|<=i or Acc(s, i+1, D(s_i,r))) + // where Acc(s, i+1, ite(c, t, f)) = ite(c, Acc(s, i+1, t), Acc(s, i+1, t)) + // and Acc(s, i+1, r U s) = Acc(s, i+1, r) or Acc(s, i+1, s) th.add_axiom(accept_next); } @@ -418,20 +427,21 @@ namespace smt { /* Wrapper around calls to is_nullable from the seq rewriter. - Note: the nullable wrapper and derivative wrapper actually use + TODO: clean up the following: + Note: the is_nullable_wrapper and mk_derivative_wrapper actually use different sequence rewriters; these are at: m_seq_rewrite (returned by seq_rw()) th.m_rewrite.m_imp->m_cfg.m_seq_rw (private, can't be accessed directly) As a result operations are cached separately for the nullable - and derivative calls. TBD if caching them using the same rewriter - makes any difference. + and derivative calls. */ expr_ref seq_regex::is_nullable_wrapper(expr* r) { STRACE("seq_regex", tout << "nullable: " << mk_pp(r, m) << std::endl;); expr_ref result = seq_rw().is_nullable(r); + //TODO: rewrite seems unnecessary here rewrite(result); STRACE("seq_regex", tout << "nullable result: " << mk_pp(result, m) << std::endl;); @@ -442,39 +452,28 @@ namespace smt { } /* - Wrapper around the regex symbolic derivative from the seq rewriter. - Ensures that the derivative is written in a normalized BDD form - with optimizations for if-then-else expressions involving the head. - - Note: the nullable wrapper and derivative wrapper actually use - different sequence rewriters; these are at: - m_seq_rewrite - (returned by seq_rw()) - th.m_rewrite.m_imp->m_cfg.m_seq_rw - (private, can't be accessed directly) - As a result operations are cached separately for the nullable - and derivative calls. TBD if caching them using the same rewriter - makes any difference. + First creates a derivatrive of r wrt x=(:var 0) and then replaces x by ele. + This will create a cached entry for the generic derivative of r that is independent of ele. */ - expr_ref seq_regex::derivative_wrapper(expr* hd, expr* r) { - STRACE("seq_regex", tout << "derivative(" << mk_pp(hd, m) << "): " << mk_pp(r, m) << std::endl;); + expr_ref seq_regex::mk_derivative_wrapper(expr* ele, expr* r) { + STRACE("seq_regex", tout << "derivative(" << mk_pp(ele, m) << "): " << mk_pp(r, m) << std::endl;); - // Use canonical variable for head - expr_ref hd_canon(m.mk_var(0, hd->get_sort()), m); - expr_ref result(re().mk_derivative(hd_canon, r), m); - rewrite(result); + // Uses canonical variable (:var 0) for the derivative element + expr_ref der(seq_rw().mk_derivative(r), m); - // Substitute with real head + // Substitute (:var 0) with the actual element var_subst subst(m); expr_ref_vector sub(m); - sub.push_back(hd); - result = subst(result, sub); + sub.push_back(ele); + der = subst(der, sub); - STRACE("seq_regex", tout << "derivative result: " << mk_pp(result, m) << std::endl;); + STRACE("seq_regex", tout << "derivative result: " << mk_pp(der, m) << std::endl;); STRACE("seq_regex_brief", tout << "d(" << state_str(r) << ")=" - << state_str(result) << " ";); + << state_str(der) << " ";); - return result; + //TODO: simplify der further, if ele implies further simplifications + //e.g. if ele='b' then de(ite (x='a') t f) simplifies to t + return der; } void seq_regex::propagate_eq(expr* r1, expr* r2) { @@ -557,7 +556,7 @@ namespace smt { literal null_lit = th.mk_literal(is_nullable); expr_ref hd = mk_first(r, n); expr_ref d(m); - d = derivative_wrapper(hd, r); + d = mk_derivative_wrapper(hd, r); literal_vector lits; lits.push_back(~lit); @@ -584,11 +583,10 @@ namespace smt { } /* - Given a string s, index i, and a derivative regex d, return an + Given a string s, index i, and a derivative r, return an expression that is equivalent to accept s i r - but which pushes accept s i r into the leaves (next derivatives to - explore). + but which pushes accept s i r into the leaves Input r is of type regex; output is of type bool. @@ -600,14 +598,18 @@ namespace smt { expr_ref seq_regex::mk_deriv_accept(expr* s, unsigned i, expr* r) { vector to_visit; to_visit.push_back(r); - obj_map re_to_bool; + obj_map re_to_accept; expr_ref_vector _temp_bool_owner(m); // temp owner for bools we create - // DFS + bool s_is_longer_than_i = str().min_length(s) > i; + expr* i_int = a().mk_int(i); + _temp_bool_owner.push_back(i_int); + + // DFS, avoids duplicating derivative construction that has already been done while (to_visit.size() > 0) { expr* e = to_visit.back(); expr* econd = nullptr, *e1 = nullptr, *e2 = nullptr; - if (!re_to_bool.contains(e)) { + if (!re_to_accept.contains(e)) { // First visit: add children STRACE("seq_regex_verbose", tout << "1";); if (m.is_ite(e, econd, e1, e2) || @@ -616,36 +618,40 @@ namespace smt { to_visit.push_back(e2); } // Mark first visit by adding nullptr to the map - re_to_bool.insert(e, nullptr); + re_to_accept.insert(e, nullptr); } - else if (re_to_bool.find(e) == nullptr) { + else if (re_to_accept.find(e) == nullptr) { // Second visit: set value STRACE("seq_regex_verbose", tout << "2";); to_visit.pop_back(); if (m.is_ite(e, econd, e1, e2)) { - expr* b1 = re_to_bool.find(e1); - expr* b2 = re_to_bool.find(e2); - expr* b = m.mk_ite(econd, b1, b2); + expr* b1 = re_to_accept.find(e1); + expr* b2 = re_to_accept.find(e2); + expr* b = m.is_true(econd) || b1 == b2 ? b1 : m.is_false(econd) ? b2 : m.mk_ite(econd, b1, b2); _temp_bool_owner.push_back(b); - re_to_bool.find(e) = b; + re_to_accept.find(e) = b; + } + else if (re().is_empty(e) || (s_is_longer_than_i && re().is_epsilon(e))) + { + // s[i..] in [] <==> false, also: s[i..] in () <==> false when |s|>i + re_to_accept.find(e) = m.mk_false(); + } + else if (re().is_full_seq(e) || (s_is_longer_than_i && re().is_dot_plus(e))) + { + // s[i..] in .* <==> true, also: s[i..] in .+ <==> true when |s|>i + re_to_accept.find(e) = m.mk_true(); } /* - else if (re().is_empty(e)) - { - re_to_bool.find(e) = m.mk_false(); - } else if (re().is_epsilon(e)) { - expr* iplus1 = a().mk_int(i); expr* one = a().mk_int(1); - _temp_bool_owner.push_back(iplus1); _temp_bool_owner.push_back(one); - //the substring starting after position iplus1 must be empty - expr* s_end = str().mk_substr(s, iplus1, one); + //the substring starting after position i must be empty + expr* s_end = str().mk_substr(s, i_int, one); expr* s_end_is_epsilon = m.mk_eq(s_end, str().mk_empty(m.get_sort(s))); _temp_bool_owner.push_back(s_end_is_epsilon); - re_to_bool.find(e) = s_end_is_epsilon; + re_to_accept.find(e) = s_end_is_epsilon; STRACE("seq_regex_verbose", tout << "added empty sequence leaf: " @@ -653,18 +659,16 @@ namespace smt { } */ else if (re().is_union(e, e1, e2)) { - expr* b1 = re_to_bool.find(e1); - expr* b2 = re_to_bool.find(e2); - expr* b = m.mk_or(b1, b2); + expr* b1 = re_to_accept.find(e1); + expr* b2 = re_to_accept.find(e2); + expr* b = m.is_false(b1) || b1 == b2 ? b2 : m.is_false(b2) ? b1 : m.mk_or(b1, b2); _temp_bool_owner.push_back(b); - re_to_bool.find(e) = b; + re_to_accept.find(e) = b; } else { - expr* iplus1 = a().mk_int(i); - _temp_bool_owner.push_back(iplus1); - expr_ref acc_leaf = sk().mk_accept(s, iplus1, e); + expr_ref acc_leaf = sk().mk_accept(s, i_int, e); _temp_bool_owner.push_back(acc_leaf); - re_to_bool.find(e) = acc_leaf; + re_to_accept.find(e) = acc_leaf; STRACE("seq_regex_verbose", tout << "mk_deriv_accept: added accept leaf: " @@ -680,59 +684,44 @@ namespace smt { // Finalize expr_ref result(m); - result = re_to_bool.find(r); // Assigns ownership of all exprs in - // re_to_bool for after this completes + result = re_to_accept.find(r); // Assigns ownership of all exprs in + // re_to_accept for after this completes rewrite(result); return result; } /* - Return a list of all leaves in the derivative of a regex r, + Return a list of all target regexes in the derivative of a regex r, ignoring the conditions along each path. - Warning: Although the derivative - normal form tries to eliminate unsat condition paths, one cannot - assume that the path to each leaf is satisfiable in general - (e.g. when regexes are created using re.pred). - So not all results may correspond to satisfiable predicates. - It is OK to rely on the results being satisfiable for completeness, - but not soundness. + The derivative construction uses (:var 0) and tries + to eliminate unsat condition paths but it does not perform + full satisfiability checks and it is not guaranteed + that all targets are actually reachable */ - void seq_regex::get_all_derivatives(expr* r, expr_ref_vector& results) { - // Get derivative - sort* seq_sort = nullptr; - VERIFY(u().is_re(r, seq_sort)); - expr_ref n(m.mk_fresh_const("re.char", seq_sort), m); - expr_ref hd = mk_first(r, n); - expr_ref d(m); - d = derivative_wrapper(hd, r); + void seq_regex::get_derivative_targets(expr* r, expr_ref_vector& targets) { + // constructs the derivative wrt (:var 0) + expr_ref d(seq_rw().mk_derivative(r), m); - // DFS - vector to_visit; - to_visit.push_back(d); - obj_map visited; // set (bool is used as a unit type) - while (to_visit.size() > 0) { - expr* e = to_visit.back(); - to_visit.pop_back(); - if (visited.contains(e)) continue; - visited.insert(e, true); - expr* econd = nullptr, *e1 = nullptr, *e2 = nullptr; - if (m.is_ite(e, econd, e1, e2) || - re().is_union(e, e1, e2)) { - to_visit.push_back(e1); - to_visit.push_back(e2); - } - else if (!re().is_empty(e)) { - results.push_back(e); - STRACE("seq_regex_verbose", tout - << "get_all_derivatives: added deriv: " - << mk_pp(e, m) << std::endl;); + // use DFS to collect all the targets (leaf regexes) in d. + expr* _1 = nullptr, * e1 = nullptr, * e2 = nullptr; + obj_hashtable::entry* _2 = nullptr; + vector workset; + workset.push_back(d); + obj_hashtable done; + done.insert(d); + while (workset.size() > 0) { + expr* e = workset.back(); + workset.pop_back(); + if (m.is_ite(e, _1, e1, e2) || re().is_union(e, e1, e2)) { + if (done.insert_if_not_there_core(e1, _2)) + workset.push_back(e1); + if (done.insert_if_not_there_core(e2, _2)) + workset.push_back(e2); } + else if (!re().is_empty(e)) + targets.push_back(e); } - - STRACE("seq_regex", tout << "Number of derivatives: " - << results.size() << std::endl;); - STRACE("seq_regex_brief", tout << "#derivs=" << results.size() << " ";); } /* @@ -800,7 +789,7 @@ namespace smt { th.add_axiom(~lit, ~th.mk_literal(is_nullable)); expr_ref hd = mk_first(r, n); expr_ref d(m); - d = derivative_wrapper(hd, r); + d = mk_derivative_wrapper(hd, r); literal_vector lits; expr_ref_pair_vector cofactors(m); get_cofactors(d, cofactors); @@ -884,7 +873,7 @@ namespace smt { STRACE("state_graph", if (!m_state_graph.is_seen(r_id)) - tout << std::endl << "state(" << r_id << ") = " << seq_util::rex::pp(re(), r) << std::endl << "info(" << r_id << ") = " << re().get_info(r) << std::endl;); + tout << std::endl << "state(" << r_id << ") = " << re().to_str(r) << std::endl << "info(" << r_id << ") = " << re().get_info(r) << std::endl;); // Add state m_state_graph.add_state(r_id); STRACE("seq_regex", tout << "Updating state graph for regex " @@ -900,14 +889,14 @@ namespace smt { expr_ref_vector derivatives(m); STRACE("seq_regex_verbose", tout << "getting all derivs: " << r_id << " " << std::endl;); - get_all_derivatives(r, derivatives); + get_derivative_targets(r, derivatives); for (auto const& dr: derivatives) { unsigned dr_id = get_state_id(dr); STRACE("seq_regex_verbose", tout << std::endl << " traversing deriv: " << dr_id << " ";); STRACE("state_graph", if (!m_state_graph.is_seen(dr_id)) - tout << "state(" << dr_id << ") = " << seq_util::rex::pp(re(), dr) << std::endl << "info(" << dr_id << ") = " << re().get_info(dr) << std::endl;); + tout << "state(" << dr_id << ") = " << re().to_str(dr) << std::endl << "info(" << dr_id << ") = " << re().get_info(dr) << std::endl;); // Add state m_state_graph.add_state(dr_id); bool maybecycle = can_be_in_cycle(r, dr); diff --git a/src/smt/seq_regex.h b/src/smt/seq_regex.h index 8a2cf68f1..dcf81dbc3 100644 --- a/src/smt/seq_regex.h +++ b/src/smt/seq_regex.h @@ -158,12 +158,12 @@ namespace smt { expr_ref symmetric_diff(expr* r1, expr* r2); expr_ref is_nullable_wrapper(expr* r); - expr_ref derivative_wrapper(expr* hd, expr* r); + expr_ref mk_derivative_wrapper(expr* hd, expr* r); // Various support for unfolding derivative expressions that are // returned by derivative_wrapper expr_ref mk_deriv_accept(expr* s, unsigned i, expr* r); - void get_all_derivatives(expr* r, expr_ref_vector& results); + void get_derivative_targets(expr* r, expr_ref_vector& targets); void get_cofactors(expr* r, expr_ref_pair_vector& result); void get_cofactors_rec(expr* r, expr_ref_vector& conds, expr_ref_pair_vector& result); From 6f5597117708748a219fbe8d89d242b3ed35b625 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 8 Oct 2021 13:06:10 -0700 Subject: [PATCH 06/89] Newderiv (#5585) * updated derivative engine * some edit * further improvements in derivative code * more deriv code edits and re::to_str update * optimized mk_deriv_accept * fixed PR comments * small syntax fix * updated some simplifications * bugfix:forgot to_re before reverse * fixed PR comments * more PR comment fixes * more PR comment fixes * forgot to delete * deleting unused definition * fixes Signed-off-by: Nikolaj Bjorner Co-authored-by: Margus Veanes From bfa960c2cee9ec4343d00f2e504508d757a36a1e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 8 Oct 2021 14:48:17 -0700 Subject: [PATCH 07/89] fix internalize regression Signed-off-by: Nikolaj Bjorner --- src/sat/smt/array_internalize.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sat/smt/array_internalize.cpp b/src/sat/smt/array_internalize.cpp index 7e3f94b84..aa94f12a7 100644 --- a/src/sat/smt/array_internalize.cpp +++ b/src/sat/smt/array_internalize.cpp @@ -120,8 +120,8 @@ namespace array { SASSERT(!n || !n->is_attached_to(get_id())); if (!n) n = mk_enode(e, false); - SASSERT(!n->is_attached_to(get_id())); - mk_var(n); + if (!n->is_attached_to(get_id())) + mk_var(n); for (auto* arg : euf::enode_args(n)) ensure_var(arg); switch (a->get_decl_kind()) { From f1b8376739f4c431c96dfea3bed0f01299cdce60 Mon Sep 17 00:00:00 2001 From: "Andrew V. Jones" Date: Sat, 9 Oct 2021 23:07:37 +0100 Subject: [PATCH 08/89] Rename 'user' to 'user_solver' #5586 (#5587) Issue #5586 reported that Android builds (targetting, e.g., x86) failed to compile due to a conflict between: * `struct user` in `sys/user.h`; and * `namespace user` in z3's `user_solver.h` This issue is resolved by renaming `namespace user` to `namespace user_solver` (matching the header name) to avoid this conflict. Reported-by: Jamie Collinson Signed-off-by: Andrew V. Jones --- src/sat/smt/euf_solver.cpp | 2 +- src/sat/smt/euf_solver.h | 2 +- src/sat/smt/user_solver.cpp | 2 +- src/sat/smt/user_solver.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 3c3b6e884..3c1831c87 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -994,7 +994,7 @@ namespace euf { ::solver::push_eh_t& push_eh, ::solver::pop_eh_t& pop_eh, ::solver::fresh_eh_t& fresh_eh) { - m_user_propagator = alloc(user::solver, *this); + m_user_propagator = alloc(user_solver::solver, *this); m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh); for (unsigned i = m_scopes.size(); i-- > 0; ) m_user_propagator->push(); diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 6d08ba512..2449335d1 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -101,7 +101,7 @@ namespace euf { sat::sat_internalizer* m_to_si; scoped_ptr m_ackerman; scoped_ptr m_dual_solver; - user::solver* m_user_propagator = nullptr; + user_solver::solver* m_user_propagator = nullptr; th_solver* m_qsolver = nullptr; unsigned m_generation = 0; mutable ptr_vector m_todo; diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index e9d8a54cb..5a1a07f11 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -18,7 +18,7 @@ Author: #include "sat/smt/user_solver.h" #include "sat/smt/euf_solver.h" -namespace user { +namespace user_solver { solver::solver(euf::solver& ctx) : th_euf_solver(ctx, symbol("user"), ctx.get_manager().mk_family_id("user")) diff --git a/src/sat/smt/user_solver.h b/src/sat/smt/user_solver.h index e16eb5b5e..2939a2f0a 100644 --- a/src/sat/smt/user_solver.h +++ b/src/sat/smt/user_solver.h @@ -23,7 +23,7 @@ Author: #include "solver/solver.h" -namespace user { +namespace user_solver { class solver : public euf::th_euf_solver, public ::solver::propagate_callback { From 0fc9f1d46a4b35744e2c9061ff86e01b36b34657 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Oct 2021 16:20:32 -0700 Subject: [PATCH 09/89] fix max/min length to handle concatenation Signed-off-by: Nikolaj Bjorner --- src/ast/seq_decl_plugin.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index cc78da8f6..8f3001055 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -932,7 +932,10 @@ unsigned seq_util::str::min_length(expr* s) const { return 0u; }; while (is_concat(s, s1, s2)) { - result += get_length(s1); + if (is_concat(s1)) + result += min_length(s1); + else + result += get_length(s1); s = s2; } result += get_length(s); @@ -960,7 +963,10 @@ unsigned seq_util::str::max_length(expr* s) const { return UINT_MAX; }; while (is_concat(s, s1, s2)) { - result = u.max_plus(get_length(s1), result); + if (is_concat(s1)) + result = u.max_plus(max_length(s1), result); + else + result = u.max_plus(get_length(s1), result); s = s2; } result = u.max_plus(get_length(s), result); From 88c3119d8d7cd164ef123103946b5b3dc5b404b2 Mon Sep 17 00:00:00 2001 From: Jamie Collinson Date: Mon, 11 Oct 2021 17:24:41 +0100 Subject: [PATCH 10/89] Create android-build.yml (#5588) --- .github/workflows/android-build.yml | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/android-build.yml diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml new file mode 100644 index 000000000..b3a17f301 --- /dev/null +++ b/.github/workflows/android-build.yml @@ -0,0 +1,35 @@ +name: Android Build + +on: + push: + branches: [ master ] + +env: + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + android-abi: [arm64-v8a, armeabi-v7a, x86, x86_64] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Configure CMake and build + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_SYSTEM_NAME=Android -DCMAKE_SYSTEM_VERSION=21 -DCMAKE_ANDROID_ARCH_ABI=${{ matrix.android-abi }} -DCMAKE_ANDROID_NDK=$ANDROID_NDK_HOME -DZ3_BUILD_JAVA_BINDINGS=TRUE -G "Unix Makefiles" -DJAVA_AWT_LIBRARY=NotNeeded -DJAVA_JVM_LIBRARY=NotNeeded -DJAVA_INCLUDE_PATH2=NotNeeded -DJAVA_AWT_INCLUDE_PATH=NotNeeded ../ + make -j $(nproc) + tar -cvf z3-build-${{ matrix.android-abi }}.tar *.jar *.so + + - name: Archive production artifacts + uses: actions/upload-artifact@v2 + with: + name: android-build-${{ matrix.android-abi }} + path: build/z3-build-${{ matrix.android-abi }}.tar From f7a2d08e74a7130c0bddc8d963b1e126d27cb20c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Oct 2021 10:32:47 -0700 Subject: [PATCH 11/89] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1eda3db92..70dc0f8a2 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z ## Build status -| Azure Pipelines | Code Coverage | Open Bugs | -| --------------- | --------------|-----------| -| [![Build Status](https://dev.azure.com/Z3Public/Z3/_apis/build/status/Z3Prover.z3?branchName=master)](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [![CodeCoverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) | +| Azure Pipelines | Code Coverage | Open Bugs | Android Build | +| --------------- | --------------|-----------|---------------| +| [![Build Status](https://dev.azure.com/Z3Public/Z3/_apis/build/status/Z3Prover.z3?branchName=master)](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [![CodeCoverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) |[![Android Build](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | [1]: #building-z3-on-windows-using-visual-studio-command-prompt [2]: #building-z3-using-make-and-gccclang From 75702c36311f8adb2edcf92548a60387c87f8f9b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 10 Oct 2021 18:29:55 -0700 Subject: [PATCH 12/89] na Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/recfun_rewriter.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/recfun_rewriter.cpp b/src/ast/rewriter/recfun_rewriter.cpp index 2519d0755..b332f2256 100644 --- a/src/ast/rewriter/recfun_rewriter.cpp +++ b/src/ast/rewriter/recfun_rewriter.cpp @@ -22,10 +22,9 @@ Author: br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (m_rec.is_defined(f) && num_args > 0) { - for (unsigned i = 0; i < num_args; ++i) { + for (unsigned i = 0; i < num_args; ++i) if (!m.is_value(args[i])) return BR_FAILED; - } if (!m_rec.has_def(f)) return BR_FAILED; recfun::def const& d = m_rec.get_def(f); @@ -35,9 +34,8 @@ br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * result = sub(d.get_rhs(), num_args, args); return BR_REWRITE_FULL; } - else { + else return BR_FAILED; - } } From 73102cffcbe2cc5dc491bbcf626c8b6939d46ac1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Oct 2021 11:03:15 -0700 Subject: [PATCH 13/89] fix #5589 --- src/ast/datatype_decl_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index 084f7d2c7..66ce1a3ba 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -406,7 +406,7 @@ namespace datatype { VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast())); VALIDATE_PARAM(u().is_datatype(domain[0])); VALIDATE_PARAM_PP(domain[0] == to_func_decl(parameters[0].get_ast())->get_range(), "invalid sort argument passed to recognizer"); - // blindly trust that parameter is a constructor + VALIDATE_PARAM_PP(u().is_constructor(to_func_decl(parameters[0].get_ast())), "expecting constructor argument to recognizer"); sort* range = m_manager->mk_bool_sort(); func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters); info.m_private_parameters = true; From c3549ec784149aeb1796b0deb4dbb0b2cc18d4b6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 11 Oct 2021 11:03:41 -0700 Subject: [PATCH 14/89] na Signed-off-by: Nikolaj Bjorner --- src/smt/smt_conflict_resolution.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 14a71ae28..4392b8a1f 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -413,6 +413,7 @@ namespace smt { // of justification are considered level zero. if (m_conflict_lvl <= m_ctx.get_search_level()) { TRACE("conflict", tout << "problem is unsat\n";); + TRACE("conflict", m_ctx.display(tout);); if (m.proofs_enabled()) mk_conflict_proof(conflict, not_l); if (m_ctx.tracking_assumptions()) @@ -1359,9 +1360,8 @@ namespace smt { void conflict_resolution::process_antecedent_for_unsat_core(literal antecedent) { bool_var var = antecedent.var(); - TRACE("conflict", tout << "processing antecedent: "; + CTRACE("conflict", !m_ctx.is_marked(var), tout << "processing antecedent: "; m_ctx.display_literal_info(tout << antecedent << " ", antecedent); - tout << (m_ctx.is_marked(var)?"marked":"not marked"); tout << "\n";); if (!m_ctx.is_marked(var)) { From 12c32663c67e48d4397759bb12c0c20f8e7bde1b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Sep 2021 11:44:39 +0000 Subject: [PATCH 15/89] Fix error messsages --- src/ast/fpa/fpa2bv_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index de0b69946..8f0820c13 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -465,7 +465,7 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, SASSERT(is_well_sorted(m, big_d_sig)); if (ebits > sbits) - throw default_exception("there is no floating point support for division for representations with non-standard bit representations"); + throw default_exception("addition/subtract with ebits > sbits not supported"); expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m); @@ -950,7 +950,7 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); if (ebits > sbits) - throw default_exception("there is no floating point support for division for representations with non-standard bit representations"); + throw default_exception("division with ebits > sbits not supported"); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); From e8d6d97ba378e4b80e4af4f7fc5296c02104d9a7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Sep 2021 11:45:17 +0000 Subject: [PATCH 16/89] Refine fpa_decl_plugin::is_unique_value --- src/ast/fpa_decl_plugin.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index 2dca61a39..5062615f2 100644 --- a/src/ast/fpa_decl_plugin.cpp +++ b/src/ast/fpa_decl_plugin.cpp @@ -207,7 +207,7 @@ sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) { m_manager->raise_exception("minimum number of exponent bits is 2"); if (ebits > 63) m_manager->raise_exception("maximum number of exponent bits is 63"); - + parameter p1(ebits), p2(sbits); parameter ps[2] = { p1, p2 }; sort_size sz; @@ -929,16 +929,22 @@ bool fpa_decl_plugin::is_unique_value(app* e) const { case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: return true; - case OP_FPA_PLUS_INF: /* No; +oo == fp(#b0 #b11 #b00) */ - case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */ - case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */ - case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */ + case OP_FPA_PLUS_INF: /* No; +oo == (fp #b0 #b11 #b00) */ + case OP_FPA_MINUS_INF: /* No; -oo == (fp #b1 #b11 #b00) */ + case OP_FPA_PLUS_ZERO: /* No; +zero == (fp #b0 #b00 #b000) */ + case OP_FPA_MINUS_ZERO: /* No; -zero == (fp #b1 #b00 #b000) */ case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */ case OP_FPA_NUM: /* see NaN */ return false; - case OP_FPA_FP: - return false; /*No; generally not because of clashes with +oo, -oo, +zero, -zero, NaN */ - // a refinement would require to return true only if there is no clash with these cases. + case OP_FPA_FP: { + if (m_manager->is_value(e->get_arg(0)) && + m_manager->is_value(e->get_arg(1)) && + m_manager->is_value(e->get_arg(2))) { + bv_util bu(*m_manager); + return !bu.is_allone(e->get_arg(1)) && !bu.is_zero(e->get_arg(1)); + } + return false; + } default: return false; } From 515a2a771e270f8c84a2d1f3e8a3c3909d33e8cd Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Sep 2021 11:45:49 +0000 Subject: [PATCH 17/89] Whitespace --- src/ast/fpa/bv2fpa_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 49f9abacd..00d75e364 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -102,7 +102,7 @@ expr_ref bv2fpa_converter::convert_bv2fp(sort * s, expr * sgn, expr * exp, expr rational exp_unbiased_q; exp_unbiased_q = exp_q - m_fpa_util.fm().m_powers2.m1(ebits - 1); - scoped_mpz sig_z(mpzm); + scoped_mpz sig_z(mpzm); mpf_exp_t exp_z; mpzm.set(sig_z, sig_q.to_mpq().numerator()); exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator()); From f1acc4b78a8c80377c8f65a7a2758f8a59b1e5b5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Sep 2021 12:55:18 +0000 Subject: [PATCH 18/89] Make fpa2bv debug symbol names optional --- src/ast/fpa/bv2fpa_converter.cpp | 9 +++++---- src/ast/fpa/fpa2bv_converter.cpp | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 00d75e364..3da45781f 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -346,7 +346,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model app * a0 = to_app(val->get_arg(0)); expr_ref v0(m), v1(m), v2(m); -#ifdef Z3DEBUG +#ifdef Z3DEBUG_FPA2BV_NAMES app * a1 = to_app(val->get_arg(1)); app * a2 = to_app(val->get_arg(2)); v0 = mc->get_const_interp(a0->get_decl()); @@ -378,7 +378,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP)); -#ifdef Z3DEBUG +#ifdef Z3DEBUG_FPA2BV_NAMES SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0); SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0); @@ -386,9 +386,10 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model seen.insert(to_app(val->get_arg(1))->get_decl()); seen.insert(to_app(val->get_arg(2))->get_decl()); #else - SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT); - SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT); + SASSERT(is_app(val->get_arg(0))); + SASSERT(m_bv_util.is_extract(val->get_arg(0))); seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl()); + #endif if (!sgn && !sig && !exp) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 8f0820c13..b35be66f6 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -192,7 +192,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { app_ref sgn(m), s(m), e(m); -#ifdef Z3DEBUG +#ifdef Z3DEBUG_FPA2BV_NAMES std::string p("fpa2bv"); std::string name = f->get_name().str(); @@ -326,7 +326,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { expr_ref bv3(m); bv3 = m.mk_fresh_const( -#ifdef Z3DEBUG +#ifdef Z3DEBUG_FPA2BV_NAMES "fpa2bv_rm" #else nullptr @@ -3839,7 +3839,7 @@ void fpa2bv_converter::mk_rounding_mode(decl_kind k, expr_ref & result) } void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { -#ifdef Z3DEBUG +#ifdef Z3DEBUG_FPA2BV_NAMES return; // CMW: This works only for quantifier-free formulas. if (m_util.is_fp(e)) { From 8e69f76784a6fb6ef9418dfdf0d2c1aca11fb9b7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Sep 2021 13:27:32 +0000 Subject: [PATCH 19/89] Add additional equality in theory_fpa --- src/smt/theory_fpa.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index 1543ac2c2..c36346de4 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -454,13 +454,9 @@ namespace smt { expr * args[] = { bv_val_a->get_arg(0), bv_val_a->get_arg(1), bv_val_a->get_arg(2) }; cc_args = m_bv_util.mk_concat(3, args); c = m.mk_eq(wrapped, cc_args); - // NB code review: #5454 exposes a bug in fpa_solver that - // could be latent here as well. It needs also the equality - // n == bv_val_e to be asserted such that whenever something is assigned th - // bit-vector value cc_args it is equated with n - // I don't see another way this constraint would be enforced. assert_cnstr(c); assert_cnstr(mk_side_conditions()); + assert_cnstr(m.mk_eq(n, bv_val_e)); } else { expr_ref wu(m); From 00e8ea79621c5029d6773e3164ded601161c0898 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 14 Sep 2021 15:46:58 +0000 Subject: [PATCH 20/89] Make terms that are internalized on the fly relevant --- src/smt/theory_fpa.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index c36346de4..d1f60eb97 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -38,7 +38,7 @@ namespace smt { { params_ref p; p.set_bool("arith_lhs", true); - m_th_rw.updt_params(p); + m_th_rw.updt_params(p); } theory_fpa::~theory_fpa() @@ -284,6 +284,9 @@ namespace smt { } default: /* ignore */; } + + if (!ctx.relevancy()) + relevant_eh(term); } return true; @@ -623,7 +626,7 @@ namespace smt { bv2fp.convert_min_max_specials(&mdl, &new_model, seen); bv2fp.convert_uf2bvuf(&mdl, &new_model, seen); - for (func_decl* f : seen) + for (func_decl* f : seen) mdl.unregister_decl(f); for (unsigned i = 0; i < new_model.get_num_constants(); i++) { From c24f438e51cc9af302e400d36e1f1b73a4bb0d9a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 15 Sep 2021 12:33:49 +0000 Subject: [PATCH 21/89] Fix for mk_to_fp_float; pertains to #4841 --- src/ast/fpa/fpa2bv_converter.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index b35be66f6..7876d2d79 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -2561,9 +2561,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r res_sig = sig; res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. - unsigned sig_sz = m_bv_util.get_bv_size(res_sig); - (void) sig_sz; - SASSERT(sig_sz == to_sbits + 4); + SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 4); expr_ref exponent_overflow(m), exponent_underflow(m); exponent_overflow = m.mk_false(); @@ -2577,7 +2575,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz); res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } - else if (from_ebits > (to_ebits + 2)) { + else if (from_ebits >= (to_ebits + 2)) { unsigned ebits_diff = from_ebits - (to_ebits + 2); // subtract lz for subnormal numbers. @@ -2617,9 +2615,6 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); res_exp = m.mk_ite(udf_cond, min_exp, res_exp); } - else { // from_ebits == (to_ebits + 2) - res_exp = m_bv_util.mk_bv_sub(exp, lz); - } SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2); SASSERT(is_well_sorted(m, res_exp)); From 738783a26c760d33a3ef18c31d0d4484f0c2f1f8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 15 Sep 2021 16:29:06 +0000 Subject: [PATCH 22/89] Fix off-by-one in fp.div bit-blasting. Inspired by #4841 but doesn't quite fix it. --- src/ast/fpa/fpa2bv_converter.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 7876d2d79..0d3f0e7c0 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -982,6 +982,8 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & // b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV_I, a_sig_ext, b_sig_ext); + dbg_decouple("fpa2bv_div_a_sig_ext", a_sig_ext); + dbg_decouple("fpa2bv_div_b_sig_ext", b_sig_ext); dbg_decouple("fpa2bv_div_quotient", quotient); SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); @@ -989,6 +991,7 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); + dbg_decouple("fpa2bv_div_sticky", sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); @@ -996,15 +999,21 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & mk_leading_zeros(res_sig, sbits + 4, res_sig_lz); dbg_decouple("fpa2bv_div_res_sig_lz", res_sig_lz); expr_ref res_sig_shift_amount(m); - res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); + res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(2, sbits + 4)); dbg_decouple("fpa2bv_div_res_sig_shift_amount", res_sig_shift_amount); expr_ref shift_cond(m); shift_cond = m_bv_util.mk_ule(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); expr_ref res_sig_shifted(m), res_exp_shifted(m); res_sig_shifted = m_bv_util.mk_bv_shl(res_sig, res_sig_shift_amount); res_exp_shifted = m_bv_util.mk_bv_sub(res_exp, m_bv_util.mk_extract(ebits + 1, 0, res_sig_shift_amount)); + dbg_decouple("fpa2bv_div_res_sig", res_sig); + dbg_decouple("fpa2bv_div_res_exp", res_exp); + dbg_decouple("fpa2bv_div_res_sig_shifted", res_sig_shifted); + dbg_decouple("fpa2bv_div_res_exp_shifted", res_exp_shifted); m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig); m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp); + dbg_decouple("fpa2bv_div_shift_cond", shift_cond); + round(s, rm, res_sgn, res_sig, res_exp, v8); From b471ebdf1cf17b4178c2b76033a8ba82315b0944 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 12 Oct 2021 12:44:16 +0000 Subject: [PATCH 23/89] Revert "Fix off-by-one in fp.div bit-blasting. Inspired by #4841 but doesn't quite fix it." This reverts commit f80fdb4ea3a762cfe95daa0321d9875cfa00c7ae. --- src/ast/fpa/fpa2bv_converter.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 0d3f0e7c0..7876d2d79 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -982,8 +982,6 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & // b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV_I, a_sig_ext, b_sig_ext); - dbg_decouple("fpa2bv_div_a_sig_ext", a_sig_ext); - dbg_decouple("fpa2bv_div_b_sig_ext", b_sig_ext); dbg_decouple("fpa2bv_div_quotient", quotient); SASSERT(m_bv_util.get_bv_size(quotient) == (sbits + sbits + extra_bits)); @@ -991,7 +989,6 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & expr_ref sticky(m); sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(extra_bits-2, 0, quotient)); res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(extra_bits+sbits+1, extra_bits-1, quotient), sticky); - dbg_decouple("fpa2bv_div_sticky", sticky); SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); @@ -999,21 +996,15 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & mk_leading_zeros(res_sig, sbits + 4, res_sig_lz); dbg_decouple("fpa2bv_div_res_sig_lz", res_sig_lz); expr_ref res_sig_shift_amount(m); - res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(2, sbits + 4)); + res_sig_shift_amount = m_bv_util.mk_bv_sub(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); dbg_decouple("fpa2bv_div_res_sig_shift_amount", res_sig_shift_amount); expr_ref shift_cond(m); shift_cond = m_bv_util.mk_ule(res_sig_lz, m_bv_util.mk_numeral(1, sbits + 4)); expr_ref res_sig_shifted(m), res_exp_shifted(m); res_sig_shifted = m_bv_util.mk_bv_shl(res_sig, res_sig_shift_amount); res_exp_shifted = m_bv_util.mk_bv_sub(res_exp, m_bv_util.mk_extract(ebits + 1, 0, res_sig_shift_amount)); - dbg_decouple("fpa2bv_div_res_sig", res_sig); - dbg_decouple("fpa2bv_div_res_exp", res_exp); - dbg_decouple("fpa2bv_div_res_sig_shifted", res_sig_shifted); - dbg_decouple("fpa2bv_div_res_exp_shifted", res_exp_shifted); m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig); m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp); - dbg_decouple("fpa2bv_div_shift_cond", shift_cond); - round(s, rm, res_sgn, res_sig, res_exp, v8); From 52032b9ef8a60b352027a05947c77d713bafb2d2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Oct 2021 10:16:15 -0700 Subject: [PATCH 24/89] #5467 --- src/ast/rewriter/seq_axioms.cpp | 6 +++--- src/ast/rewriter/seq_eq_solver.cpp | 9 ++++++--- src/ast/rewriter/seq_skolem.cpp | 4 ++-- src/ast/rewriter/seq_skolem.h | 6 ++++-- src/smt/seq_eq_solver.cpp | 5 +++-- src/smt/smt_conflict_resolution.cpp | 4 +++- src/smt/smt_context_pp.cpp | 12 ++++++++++-- src/smt/smt_internalizer.cpp | 2 +- src/smt/theory_seq.cpp | 11 ++++++----- 9 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index 14e6290c6..557541027 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -1142,8 +1142,8 @@ namespace seq { /** ~contains(a, b) => ~prefix(b, a) - ~contains(a, b) => ~contains(tail(a), b) or a = empty - ~contains(a, b) & a = empty => b != empty + ~contains(a, b) => ~contains(tail(a), b) + a = empty => tail(a) = empty ~(a = empty) => a = head + tail */ void axioms::unroll_not_contains(expr* e) { @@ -1165,7 +1165,7 @@ namespace seq { expr_ref bound_tracker = m_sk.mk_length_limit(s, k); expr* s0 = nullptr; if (seq.str.is_stoi(s, s0)) - s = s0; + s = s0; add_clause(~bound_tracker, mk_le(mk_len(s), k)); return bound_tracker; } diff --git a/src/ast/rewriter/seq_eq_solver.cpp b/src/ast/rewriter/seq_eq_solver.cpp index e293743f8..ca96512f0 100644 --- a/src/ast/rewriter/seq_eq_solver.cpp +++ b/src/ast/rewriter/seq_eq_solver.cpp @@ -15,6 +15,7 @@ Author: --*/ +#include "ast/ast_pp.h" #include "ast/rewriter/seq_eq_solver.h" #include "ast/bv_decl_plugin.h" @@ -675,7 +676,7 @@ namespace seq { if (rs.size() > i) { unsigned diff = rs.size() - (i + 1); for (unsigned j = 0; same && j < i; ++j) - same = !m.are_distinct(ls[j], rs[diff + j]); + same = !m.are_distinct(ls[j], rs[diff + j]); } // ls = x ++ rs ++ y, diff = |x| else { @@ -704,8 +705,9 @@ namespace seq { bool same = true; // ls = x ++ rs' && rs = rs' ++ y, diff = |x| if (rs.size() > i) { - for (unsigned j = 1; same && j <= i; ++j) - same = !m.are_distinct(ls[diff + j], rs[j]); + for (unsigned j = 1; same && j <= i; ++j) { + same = !m.are_distinct(ls[diff + j], rs[j]); + } } // ls = x ++ rs ++ y, diff = |x| else { @@ -715,6 +717,7 @@ namespace seq { if (same) return true; } + return false; } diff --git a/src/ast/rewriter/seq_skolem.cpp b/src/ast/rewriter/seq_skolem.cpp index ecefe8374..90b33c24e 100644 --- a/src/ast/rewriter/seq_skolem.cpp +++ b/src/ast/rewriter/seq_skolem.cpp @@ -28,8 +28,8 @@ skolem::skolem(ast_manager& m, th_rewriter& rw): m_tail = "seq.tail"; m_seq_first = "seq.first"; m_seq_last = "seq.last"; - m_indexof_left = "seq.idx.left"; - m_indexof_right = "seq.idx.right"; + m_indexof_left = "seq.idx.l"; + m_indexof_right = "seq.idx.r"; m_aut_step = "aut.step"; m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l m_post = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l diff --git a/src/ast/rewriter/seq_skolem.h b/src/ast/rewriter/seq_skolem.h index a7d4be917..1a5928a0b 100644 --- a/src/ast/rewriter/seq_skolem.h +++ b/src/ast/rewriter/seq_skolem.h @@ -77,8 +77,10 @@ namespace seq { expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); } expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); } - expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_left", t, s, offset); } - expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_right", t, s, offset); } + expr_ref mk_contains_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.l", t, s, offset); } + expr_ref mk_contains_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.r", t, s, offset); } + expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.l", t, s, offset); } + expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.r", t, s, offset); } expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); } expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); } diff --git a/src/smt/seq_eq_solver.cpp b/src/smt/seq_eq_solver.cpp index a8743d21c..d53773f0d 100644 --- a/src/smt/seq_eq_solver.cpp +++ b/src/smt/seq_eq_solver.cpp @@ -684,11 +684,12 @@ bool theory_seq::branch_quat_variable(depeq const& e) { cond = true; } // xs and ys cannot align - else if (!can_align_from_lhs(xs, ys) && !can_align_from_rhs(xs, ys)) + else if (!can_align_from_lhs(xs, ys) && !can_align_from_rhs(xs, ys) && !can_align_from_lhs(ys, xs) && !can_align_from_rhs(ys, xs)) cond = true; if (!cond) return false; + literal_vector lits; if (xs == ys) { @@ -724,7 +725,7 @@ bool theory_seq::branch_quat_variable(depeq const& e) { } } else { - TRACE("seq", tout << mk_pp(x1, m) << " > " << mk_pp(y1, m) << "\n";); + TRACE("seq", tout << mk_pp(x1, m) << " >\n" << mk_pp(y1, m) << "\n";); if (ctx.get_assignment(lit3) == l_undef) { ctx.mark_as_relevant(lit3); return true; diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 4392b8a1f..45ff1900d 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -474,7 +474,8 @@ namespace smt { TRACE("conflict", tout << "new scope level: " << m_new_scope_lvl << "\n"; - tout << "intern. scope level: " << m_lemma_iscope_lvl << "\n";); + tout << "intern. scope level: " << m_lemma_iscope_lvl << "\n"; + tout << "lemma: " << m_lemma << "\n";); if (m.proofs_enabled()) mk_conflict_proof(conflict, not_l); @@ -761,6 +762,7 @@ namespace smt { m_lemma .shrink(j); m_lemma_atoms.shrink(j); m_ctx.m_stats.m_num_minimized_lits += sz - j; + TRACE("conflict", tout << "lemma: " << m_lemma << "\n";); } /** diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 3f826c2d1..69c773460 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -113,9 +113,17 @@ namespace smt { } std::ostream& context::display_literals_smt2(std::ostream& out, unsigned num_lits, literal const* lits) const { - for (unsigned i = 0; i < num_lits; ++i) { + out << literal_vector(num_lits, lits) << ":\n"; +#if 1 + expr_ref_vector fmls(m); + for (unsigned i = 0; i < num_lits; ++i) + fmls.push_back(literal2expr(lits[i])); + expr_ref c = mk_or(fmls); + out << c << "\n"; +#else + for (unsigned i = 0; i < num_lits; ++i) display_literal_smt2(out, lits[i]) << "\n"; - } +#endif return out; } diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 4a4aed0f4..77b3f14d5 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1387,7 +1387,7 @@ namespace smt { default: break; } - TRACE("mk_clause", display_literals_verbose(tout << "after simplification:\n", num_lits, lits) << "\n";); + TRACE("mk_clause", display_literals_verbose(tout << "after simplification: " << literal_vector(num_lits, lits) << "\n", num_lits, lits) << "\n";); unsigned activity = 1; bool lemma = is_lemma(k); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index b48d0d29f..70f7baecd 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -726,7 +726,7 @@ void theory_seq::linearize(dependency* dep, enode_pair_vector& eqs, literal_vect svector assumptions; const_cast(m_dm).linearize(dep, assumptions); for (assumption const& a : assumptions) { - if (a.lit != null_literal) { + if (a.lit != null_literal && a.lit != true_literal) { lits.push_back(a.lit); SASSERT(ctx.get_assignment(a.lit) == l_true); } @@ -743,7 +743,7 @@ bool theory_seq::propagate_lit(dependency* dep, unsigned n, literal const* _lits return false; if (ctx.get_assignment(lit) == l_true) return false; - + literal_vector lits(n, _lits); if (lit == false_literal) { @@ -2858,7 +2858,7 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter } void theory_seq::add_axiom(literal_vector & lits) { - TRACE("seq", ctx.display_literals_verbose(tout << "assert:", lits) << "\n";); + TRACE("seq", ctx.display_literals_verbose(tout << "assert " << lits << " :", lits) << "\n";); for (literal lit : lits) ctx.mark_as_relevant(lit); @@ -2898,6 +2898,7 @@ bool theory_seq::propagate_eq(dependency* deps, literal_vector const& _lits, exp if (n1->get_root() == n2->get_root()) { return false; } + ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); @@ -2979,8 +2980,8 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { m_rewrite(se1); m_rewrite(se2); if (is_true) { - expr_ref f1 = m_sk.mk_indexof_left(se1, se2); - expr_ref f2 = m_sk.mk_indexof_right(se1, se2); + expr_ref f1 = m_sk.mk_contains_left(se1, se2); + expr_ref f2 = m_sk.mk_contains_right(se1, se2); f = mk_concat(f1, se2, f2); propagate_eq(lit, f, e1, true); propagate_eq(lit, mk_len(f), mk_len(e1), false); From 9a76bf0aa24185229e8d900d1025a130a25dbba1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Oct 2021 13:59:28 -0700 Subject: [PATCH 25/89] #5591 nth issue --- src/ast/rewriter/seq_rewriter.cpp | 26 ++++++++++++++++++-------- src/ast/seq_decl_plugin.h | 1 + 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index a956718dc..38fa1e1d1 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -1603,23 +1603,33 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) { result = s; return BR_DONE; } - if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1)) { + if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1) && pos1 > 0) { expr_ref_vector lens(m()); rational pos2; + /* + * nth(s[k, |s| - k], b) = + * b < 0 -> nth_u(a, b) + * b + k < |s| -> nth(s, b + k) + * k >= |s| -> nth_u(empty, b) + * k < |s| <= b + k -> nth_u(a, b) + */ if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) { - expr_ref idx(m_autil.mk_int(pos1), m()); - idx = m_autil.mk_add(b, idx); - expr* es[2] = { s, idx }; - result = m().mk_app(m_util.get_family_id(), OP_SEQ_NTH, 2, es); + expr_ref k(m_autil.mk_int(pos1), m()); + expr_ref case1(str().mk_nth_i(s, m_autil.mk_add(b, k)), m()); + expr_ref case2(str().mk_nth_u(str().mk_empty(s->get_sort()), b), m()); + expr_ref case3(str().mk_nth_u(a, b), m()); + result = case3; + result = m().mk_ite(m_autil.mk_lt(m_autil.mk_add(k, b), str().mk_length(s)), case1, result); + result = m().mk_ite(m_autil.mk_ge(k, str().mk_length(s)), case2, result); + result = m().mk_ite(m_autil.mk_lt(b, zero()), case3, result); return BR_REWRITE_FULL; } } - expr* es[2] = { a, b}; expr* la = str().mk_length(a); result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))), - m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_I, 2, es), - m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_U, 2, es)); + str().mk_nth_i(a, b), + str().mk_nth_u(a, b)); return BR_REWRITE_FULL; } diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 6bffbf62e..16edebee9 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -286,6 +286,7 @@ public: app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); } app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); } app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); } + app* mk_nth_u(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_U, 2, es); } app* mk_nth_c(expr* s, unsigned i) const; app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); } From c15968aa9eb1a39d0452f317151987c8fe357134 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Oct 2021 17:10:04 -0700 Subject: [PATCH 26/89] fix #4901 --- src/ast/special_relations_decl_plugin.h | 1 + src/smt/smt_context.cpp | 8 ++++++++ src/smt/smt_context.h | 3 +++ src/smt/smt_model_finder.cpp | 13 +++++++++++++ src/smt/theory_special_relations.cpp | 8 +++++++- src/smt/theory_special_relations.h | 2 ++ 6 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index 4ce7dfec2..d804fa179 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -87,6 +87,7 @@ public: bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } sr_property get_property(func_decl* f) const; sr_property get_property(app* e) const { return get_property(e->get_decl()); } + func_decl* get_relation(func_decl* f) const { SASSERT(is_special_relation(f)); return to_func_decl(f->get_parameter(0).get_ast()); } func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); } func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); } diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 71aeebd6a..87f21e660 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -34,6 +34,7 @@ Revision History: #include "smt/smt_context.h" #include "smt/smt_quick_checker.h" #include "smt/uses_theory.h" +#include "smt/theory_special_relations.h" #include "smt/smt_for_each_relevant_expr.h" #include "smt/smt_model_generator.h" #include "smt/smt_model_checker.h" @@ -1543,6 +1544,13 @@ namespace smt { } } + void context::get_specrels(func_decl_set& rels) const { + family_id fid = m.get_family_id("specrels"); + if (th) + dynamic_cast(th)->get_specrels(rels); + } + + void context::relevant_eh(expr * n) { if (b_internalized(n)) { bool_var v = get_bool_var(n); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index a607deab3..d8eea758f 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -279,6 +279,9 @@ namespace smt { return m_app2enode[n->get_id()]; } + void get_specrels(func_decl_set& rels) const; + + /** \brief Similar to get_enode, but returns 0 if n is to e_internalized. */ diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index ed663fbea..0b973f4ed 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -401,6 +401,7 @@ namespace smt { expr_ref_vector* m_new_constraints{ nullptr }; random_gen m_rand; + func_decl_set m_specrels; void reset_sort2k() { @@ -470,6 +471,7 @@ namespace smt { ast_manager& get_manager() const { return m; } void reset() { + m_specrels.reset(); flush_nodes(); m_nodes.reset(); m_next_node_id = 0; @@ -479,6 +481,11 @@ namespace smt { reset_sort2k(); } + void set_specrels(context& c) { + m_specrels.reset(); + c.get_specrels(m_specrels); + } + void set_model(proto_model* m) { reset_eval_cache(); m_model = m; @@ -1049,9 +1056,13 @@ namespace smt { */ void complete_partial_funcs(func_decl_set const& partial_funcs) { for (func_decl* f : partial_funcs) { + // Complete the current interpretation m_model->complete_partial_func(f, true); + if (m_specrels.contains(f)) + continue; + unsigned arity = f->get_arity(); func_interp* fi = m_model->get_func_interp(f); if (fi->is_constant()) @@ -2399,9 +2410,11 @@ namespace smt { } } + void model_finder::process_auf(ptr_vector const& qs, proto_model* mdl) { m_auf_solver->reset(); m_auf_solver->set_model(mdl); + m_auf_solver->set_specrels(*m_context); for (quantifier* q : qs) { quantifier_info* qi = get_quantifier_info(q); diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 78d5984e5..b25c2bac8 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -1146,5 +1146,11 @@ namespace smt { expr* e = ctx.bool_var2expr(a.var()); out << (a.phase() ? "" : "(not ") << mk_pp(e, get_manager()) << (a.phase() ? "" : ")") << "\n"; } - + + + void theory_special_relations::get_specrels(func_decl_set& rels) const { + for (auto [f, r] : m_relations) + rels.insert(m_util.get_relation(r->m_decl)); + } + } diff --git a/src/smt/theory_special_relations.h b/src/smt/theory_special_relations.h index 44eee7395..73e889a5d 100644 --- a/src/smt/theory_special_relations.h +++ b/src/smt/theory_special_relations.h @@ -199,6 +199,8 @@ namespace smt { bool can_propagate() override { return m_can_propagate; } void propagate() override; void display(std::ostream & out) const override; + + void get_specrels(func_decl_set& rels) const; }; } From 96e117d78cfb3a391d77fe954629b34e6196b66d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Oct 2021 17:10:12 -0700 Subject: [PATCH 27/89] Update smt_context.cpp --- src/smt/smt_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 87f21e660..ee3341e43 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1546,6 +1546,7 @@ namespace smt { void context::get_specrels(func_decl_set& rels) const { family_id fid = m.get_family_id("specrels"); + theory* th = get_theory(fid); if (th) dynamic_cast(th)->get_specrels(rels); } From fd77f0c1116d0fa0a62a804217137ba15ece8b12 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Oct 2021 17:16:34 -0700 Subject: [PATCH 28/89] fix #5594 --- src/opt/maxlex.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/opt/maxlex.cpp b/src/opt/maxlex.cpp index 9b73df8ca..a545aa92e 100644 --- a/src/opt/maxlex.cpp +++ b/src/opt/maxlex.cpp @@ -131,16 +131,16 @@ namespace opt { soft.set_value(l_undef); } model_ref mdl; - s().get_model(mdl); + s().get_model(mdl); if (mdl) { + TRACE("opt", tout << *mdl << "\n";); for (auto & soft : m_soft) { - if (!mdl->is_true(soft.s)) { + if (!mdl->is_true(soft.s)) break; - } soft.set_value(l_true); assert_value(soft); } - update_bounds(); + update_assignment(); } } From 7b341313d553e503acd1183106f27c04c0b34dd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Oct 2021 17:50:56 -0700 Subject: [PATCH 29/89] #5593 --- src/smt/theory_array_full.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 15214a81b..6e7403737 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -707,6 +707,38 @@ namespace smt { } else if (!has_large_domain(store_app)) { // + // let A = store(B, i, v) + // + // Add: + // default(A) = A[epsilon] + // default(B) = B[epsilon] + // + // + expr_ref_vector args1(m), args2(m); + args1.push_back(store_app->get_arg(0)); + args2.push_back(store_app); + + for (unsigned i = 1; i + 1 < num_args; ++i) { + expr* arg = store_app->get_arg(i); + sort* srt = arg->get_sort(); + auto ep = mk_epsilon(srt); + args1.push_back(ep.first); + args2.push_back(ep.first); + } + app_ref sel1(m), sel2(m); + sel1 = mk_select(args1); + sel2 = mk_select(args2); + ctx.internalize(def1, false); + ctx.internalize(def2, false); + is_new = try_assign_eq(def1, sel1) || try_assign_eq(def2, sel2); + return is_new; + + +#if 0 + // + // This is incorrect, issue #5593. + // + // let A = store(B, i, v) // // Add: @@ -731,7 +763,9 @@ namespace smt { app_ref sel1(m), sel2(m); sel1 = mk_select(args1); sel2 = mk_select(args2); + std::cout << "small domain " << sel1 << " " << sel2 << "\n"; is_new = try_assign_eq(sel1, sel2); +#endif } ctx.internalize(def1, false); From f60ed2ce928b43a40b3274036f78ffc85c36a75f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 13 Oct 2021 21:38:36 -0700 Subject: [PATCH 30/89] #5591 --- src/smt/theory_seq.cpp | 93 +++++++++++++++++++++++------------------- src/smt/theory_seq.h | 1 + 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 70f7baecd..dc9cdf083 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -562,55 +562,66 @@ void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { /* \brief Check extensionality (for sequences). */ + +bool theory_seq::check_extensionality(expr* e1, enode* n1, enode* n2) { + dependency* dep = nullptr; + expr* o1 = n1->get_expr(); + expr* o2 = n2->get_expr(); + if (o1->get_sort() != o2->get_sort()) + return true; + if (ctx.is_diseq(n1, n2) || m_exclude.contains(o1, o2)) + return true; + expr_ref e2(m); + if (!canonize(n2->get_expr(), dep, e2)) + return false; + m_new_eqs.reset(); + bool change = false; + if (!m_seq_rewrite.reduce_eq(e1, e2, m_new_eqs, change)) { + TRACE("seq", tout << "exclude " << mk_pp(o1, m) << " " << mk_pp(o2, m) << "\n";); + m_exclude.update(o1, o2); + return true; + } + for (auto const& p : m_new_eqs) { + if (m_exclude.contains(p.first, p.second)) { + TRACE("seq", tout << "excluded " << mk_pp(p.first, m) << " " << mk_pp(p.second, m) << "\n";); + return true; + } + } + ctx.assume_eq(n1, n2); + return false; +} + bool theory_seq::check_extensionality() { unsigned sz = get_num_vars(); unsigned_vector seqs; + dependency* dep = nullptr; + expr_ref e1(m); for (unsigned v = 0; v < sz; ++v) { enode* n1 = get_enode(v); expr* o1 = n1->get_expr(); - if (n1 != n1->get_root()) { - continue; - } - if (!seqs.empty() && ctx.is_relevant(n1) && m_util.is_seq(o1) && ctx.is_shared(n1)) { - dependency* dep = nullptr; - expr_ref e1(m); - if (!canonize(o1, dep, e1)) { + // check extensionality for sequence arguments of nth_u + // two occurrences of nth_u(a,x), nth_u(b,y) must induce comparing sequences a, b + // whenever x = y + if (m_util.str.is_nth_u(o1) && n1->is_cgr()) { + auto* r0 = n1->get_arg(0)->get_root(); + auto* r1 = n1->get_arg(1)->get_root(); + if (!canonize(r0->get_expr(), dep, e1)) return false; - } - for (theory_var v : seqs) { - enode* n2 = get_enode(v); - expr* o2 = n2->get_expr(); - if (o1->get_sort() != o2->get_sort()) { - continue; - } - if (ctx.is_diseq(n1, n2) || m_exclude.contains(o1, o2)) { - continue; - } - expr_ref e2(m); - if (!canonize(n2->get_expr(), dep, e2)) { + for (auto* p : r1->get_parents()) + if (p != n1 && p->is_cgr() && m_util.str.is_nth_u(p->get_expr()) && + !check_extensionality(e1, r0, p->get_arg(0)->get_root())) return false; - } - m_new_eqs.reset(); - bool change = false; - if (!m_seq_rewrite.reduce_eq(e1, e2, m_new_eqs, change)) { - TRACE("seq", tout << "exclude " << mk_pp(o1, m) << " " << mk_pp(o2, m) << "\n";); - m_exclude.update(o1, o2); - continue; - } - bool excluded = false; - for (auto const& p : m_new_eqs) { - if (m_exclude.contains(p.first, p.second)) { - TRACE("seq", tout << "excluded " << mk_pp(p.first, m) << " " << mk_pp(p.second, m) << "\n";); - excluded = true; - break; - } - } - if (excluded) { - continue; - } - ctx.assume_eq(n1, n2); + } + if (!n1->is_root()) + continue; + if (!m_util.is_seq(o1)) + continue; + if (!seqs.empty() && ctx.is_relevant(n1) && ctx.is_shared(n1)) { + if (!canonize(o1, dep, e1)) return false; - } + for (theory_var v : seqs) + if (!check_extensionality(e1, n1, get_enode(v))) + return false; } seqs.push_back(v); } @@ -2368,7 +2379,7 @@ void theory_seq::validate_fmls(enode_pair_vector const& eqs, literal_vector cons theory_var theory_seq::mk_var(enode* n) { expr* o = n->get_expr(); - if (!m_util.is_seq(o) && !m_util.is_re(o)) + if (!m_util.is_seq(o) && !m_util.is_re(o) && !m_util.str.is_nth_u(o)) return null_theory_var; if (is_attached_to_var(n)) diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index ad6ef0695..68e9f3762 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -458,6 +458,7 @@ namespace smt { void add_unhandled_expr(expr* e); bool check_extensionality(); + bool check_extensionality(expr* e1, enode* n1, enode* n2); bool check_contains(); bool check_lts(); dependency* m_eq_deps { nullptr }; From 6302b864c807741223b0bf8ade4beb1da9e4f09d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 14 Oct 2021 15:46:14 -0400 Subject: [PATCH 31/89] tweak GC in OCaml bindings (#5600) * feat(api/ml): use custom block hints to guide the GC this forces the GC to collect garbage when a few _large_ objects (solver, etc.) are dead. The current code would let arbitrarily many such objects die and not trigger a GC (which would have to come from OCaml code instead) * tuning * try to use caml_alloc_custom_mem with fake sizes * try to fix leak by explicitly finalizing OCaml context * chore: use more recent ubuntu for azure CI * remove finalizer causing segfault in example --- azure-pipelines.yml | 8 ++--- src/api/ml/z3native_stubs.c.pre | 54 ++++++++++++++++----------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ed2a7d0c3..ee0001385 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -43,15 +43,15 @@ jobs: - ${{if eq(variables['runRegressions'], 'True')}}: - template: scripts/test-regressions.yml -- job: "Ubuntu18Python" - displayName: "Ubuntu 18 with ocaml" +- job: "Ubuntu20OCaml" + displayName: "Ubuntu 20 with ocaml" pool: - vmImage: "Ubuntu-18.04" + vmImage: "Ubuntu-20.04" steps: - script: sudo apt-get install ocaml opam libgmp-dev - script: opam init -y - script: eval `opam config env`; opam install zarith ocamlfind -y - - script: python scripts/mk_make.py --ml --staticlib + - script: eval `opam config env`; python scripts/mk_make.py --ml --staticlib - script: | set -e cd build diff --git a/src/api/ml/z3native_stubs.c.pre b/src/api/ml/z3native_stubs.c.pre index 5c1a3bd06..895e20235 100644 --- a/src/api/ml/z3native_stubs.c.pre +++ b/src/api/ml/z3native_stubs.c.pre @@ -76,14 +76,14 @@ int compare_pointers(void* pt1, void* pt2) { return +1; } -#define MK_CTX_OF(X) \ +#define MK_CTX_OF(X, USED) \ CAMLprim DLL_PUBLIC value n_context_of_ ## X(value v) { \ CAMLparam1(v); \ CAMLlocal1(result); \ Z3_context_plus cp; \ Z3_ ## X ## _plus * p = (Z3_ ## X ## _plus *) Data_custom_val(v); \ cp = p->cp; \ - result = caml_alloc_custom(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), 0, 1); \ + result = caml_alloc_custom_mem(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), USED); \ *(Z3_context_plus *)Data_custom_val(result) = cp; \ /* We increment the usage counter of the context, as we just \ created a second custom block holding that context */ \ @@ -102,7 +102,7 @@ int compare_pointers(void* pt1, void* pt2) { CAMLlocal1(result); \ Z3_context_plus cp = *(Z3_context_plus*)(Data_custom_val(v)); \ Z3_ ## X ## _plus a = Z3_ ## X ## _plus_mk(cp, NULL); \ - result = caml_alloc_custom(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), 0, 1); \ + result = caml_alloc_custom_mem(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), USED); \ *(Z3_ ## X ## _plus*)(Data_custom_val(result)) = a; \ CAMLreturn(result); \ } @@ -294,9 +294,9 @@ static struct custom_operations Z3_ast_plus_custom_ops = { Z3_ast_compare_ext }; -MK_CTX_OF(ast) +MK_CTX_OF(ast, 16) // let's say 16 bytes per ast -#define MK_PLUS_OBJ_NO_REF(X) \ +#define MK_PLUS_OBJ_NO_REF(X, USED) \ typedef struct { \ Z3_context_plus cp; \ Z3_ ## X p; \ @@ -349,9 +349,9 @@ MK_CTX_OF(ast) Z3_ ## X ## _compare_ext \ }; \ \ - MK_CTX_OF(X) + MK_CTX_OF(X, USED) -#define MK_PLUS_OBJ(X) \ +#define MK_PLUS_OBJ(X, USED) \ typedef struct { \ Z3_context_plus cp; \ Z3_ ## X p; \ @@ -408,27 +408,27 @@ MK_CTX_OF(ast) Z3_ ## X ## _compare_ext \ }; \ \ - MK_CTX_OF(X) + MK_CTX_OF(X, USED) -MK_PLUS_OBJ_NO_REF(symbol) -MK_PLUS_OBJ_NO_REF(constructor) -MK_PLUS_OBJ_NO_REF(constructor_list) -MK_PLUS_OBJ_NO_REF(rcf_num) -MK_PLUS_OBJ(params) -MK_PLUS_OBJ(param_descrs) -MK_PLUS_OBJ(model) -MK_PLUS_OBJ(func_interp) -MK_PLUS_OBJ(func_entry) -MK_PLUS_OBJ(goal) -MK_PLUS_OBJ(tactic) -MK_PLUS_OBJ(probe) -MK_PLUS_OBJ(apply_result) -MK_PLUS_OBJ(solver) -MK_PLUS_OBJ(stats) -MK_PLUS_OBJ(ast_map) -MK_PLUS_OBJ(ast_vector) -MK_PLUS_OBJ(fixedpoint) -MK_PLUS_OBJ(optimize) +MK_PLUS_OBJ_NO_REF(symbol, 32) +MK_PLUS_OBJ_NO_REF(constructor, 32) +MK_PLUS_OBJ_NO_REF(constructor_list, 32) +MK_PLUS_OBJ_NO_REF(rcf_num, 32) +MK_PLUS_OBJ(params, 128) +MK_PLUS_OBJ(param_descrs, 128) +MK_PLUS_OBJ(model, 512) +MK_PLUS_OBJ(func_interp, 128) +MK_PLUS_OBJ(func_entry, 128) +MK_PLUS_OBJ(goal, 128) +MK_PLUS_OBJ(tactic, 128) +MK_PLUS_OBJ(probe, 128) +MK_PLUS_OBJ(apply_result, 128) +MK_PLUS_OBJ(solver, 20 * 1000 * 1000) // pretend a solver is 20MB +MK_PLUS_OBJ(stats, 128) +MK_PLUS_OBJ(ast_map, 1024 * 2) +MK_PLUS_OBJ(ast_vector, 128) +MK_PLUS_OBJ(fixedpoint, 20 * 1000 * 1000) +MK_PLUS_OBJ(optimize, 20 * 1000 * 1000) #ifdef __cplusplus extern "C" { From cb120c93f4f8ca0c62b5162486c30a893d4132e6 Mon Sep 17 00:00:00 2001 From: Margus Veanes Date: Fri, 15 Oct 2021 15:30:55 -0700 Subject: [PATCH 32/89] Regex range bug fix (#5601) * added a missing derivative case for nonground range * further missing cases and a bug fix in re.to_str --- src/ast/rewriter/seq_rewriter.cpp | 44 +++++++++++++++++++++++++------ src/ast/seq_decl_plugin.cpp | 28 ++++++++++---------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 38fa1e1d1..237cc5d11 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -3126,21 +3126,49 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref } else if (re().is_range(r, r1, r2)) { expr_ref range(m()); - expr_ref psi(m()); + expr_ref psi(m().mk_false(), m()); if (str().is_unit_string(r1, c1) && str().is_unit_string(r2, c2)) { SASSERT(u().is_char(c1)); SASSERT(u().is_char(c2)); - // range represents c1 <= e <= c2 + // case: c1 <= e <= c2 range = simplify_path(m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2))); psi = simplify_path(m().mk_and(path, range)); - if (m().is_false(psi)) - result = nothing(); - else - // D(e,c1..c2) = if (c1<=e<=c2) then () else [] - result = re().mk_ite_simplify(range, epsilon(), nothing()); } - else + else if (!str().is_string(r1) && str().is_unit_string(r2, c2)) { + SASSERT(u().is_char(c2)); + // r1 nonground: |r1|=1 & r1[0] <= e <= c2 + expr_ref one(m_autil.mk_int(1), m()); + expr_ref zero(m_autil.mk_int(0), m()); + expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m()); + expr_ref r1_0(str().mk_nth_i(r1, zero), m()); + range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, c2)))); + psi = simplify_path(m().mk_and(path, range)); + } + else if (!str().is_string(r2) && str().is_unit_string(r1, c1)) { + SASSERT(u().is_char(c1)); + // r2 nonground: |r2|=1 & c1 <= e <= r2_0 + expr_ref one(m_autil.mk_int(1), m()); + expr_ref zero(m_autil.mk_int(0), m()); + expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m()); + expr_ref r2_0(str().mk_nth_i(r2, zero), m()); + range = simplify_path(m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(c1, e), u().mk_le(e, r2_0)))); + psi = simplify_path(m().mk_and(path, range)); + } + else if (!str().is_string(r1) && !str().is_string(r2)) { + // both r1 and r2 nonground: |r1|=1 & |r2|=1 & r1[0] <= e <= r2[0] + expr_ref one(m_autil.mk_int(1), m()); + expr_ref zero(m_autil.mk_int(0), m()); + expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m()); + expr_ref r1_0(str().mk_nth_i(r1, zero), m()); + expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m()); + expr_ref r2_0(str().mk_nth_i(r2, zero), m()); + range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, r2_0))))); + psi = simplify_path(m().mk_and(path, range)); + } + if (m().is_false(psi)) result = nothing(); + else + result = re().mk_ite_simplify(range, epsilon(), nothing()); } else if (re().is_union(r, r1, r2)) result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path)); diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 8f3001055..5245ea827 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -1233,11 +1233,11 @@ std::ostream& seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const { print(out, e); out << "[" << mk_pp(i, re.m) << "]"; } - else if (re.m.is_value(e)) - out << mk_pp(e, re.m); - else if (is_app(e)) { - out << "(" << to_app(e)->get_decl()->get_name().str(); - for (expr * arg : *to_app(e)) + else if (re.m.is_value(s)) + out << mk_pp(s, re.m); + else if (is_app(s)) { + out << "(" << to_app(s)->get_decl()->get_name().str(); + for (expr * arg : *to_app(s)) print(out << " ", arg); out << ")"; } @@ -1266,11 +1266,11 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { else if (re.is_range(e, s, s2)) print_range(out, s, s2); else if (re.is_epsilon(e)) - // ε = epsilon - out << (html_encode ? "ε" : "()"); + // ε = epsilon + out << (html_encode ? "ε" : "()"); else if (re.is_empty(e)) - // ∅ = emptyset - out << (html_encode ? "∅" : "[]"); + // ∅ = emptyset + out << (html_encode ? "∅" : "[]"); else if (re.is_concat(e, r1, r2)) { print(out, r1); print(out, r2); @@ -1278,7 +1278,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) { out << "("; print(out, r1); - out << (html_encode ? "⋃" : "|"); + out << (html_encode ? "⋃" : "|"); print(out, r2); out << ")"; } @@ -1286,7 +1286,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { { out << "("; print(out, r1); - out << (html_encode ? "⋂" : "&"); + out << (html_encode ? "⋂" : "&"); print(out, r2); out << ")"; } @@ -1359,11 +1359,11 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { out << ")"; } else if (re.m.is_ite(e, s, r1, r2)) { - out << (html_encode ? "(𝐢𝐟 " : "(if "); + out << (html_encode ? "(𝐢𝐟 " : "(if "); print(out, s); - out << (html_encode ? " 𝐭𝗵𝐞𝐧 " : " then "); + out << (html_encode ? " 𝐭𝗵𝐞𝐧 " : " then "); print(out, r1); - out << (html_encode ? " 𝐞𝐥𝘀𝐞 " : " else "); + out << (html_encode ? " 𝐞𝐥𝘀𝐞 " : " else "); print(out, r2); out << ")"; } From fb9fa1b7d2e8bc6926e8286eb226d4d0d888e802 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 15 Oct 2021 17:56:54 -0700 Subject: [PATCH 33/89] updated printer --- src/ast/seq_decl_plugin.cpp | 94 +++++++++++++++++++------------------ src/ast/seq_decl_plugin.h | 4 +- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 5245ea827..52edbdc80 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -1111,24 +1111,23 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) { /* Produces compact view of concrete concatenations such as (abcd). */ -std::ostream& seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const { - SASSERT(re.u.is_seq(s)); +bool seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const { zstring z; expr* x, * j, * k, * l, * i, * x_; if (re.u.str.is_empty(s)) out << "()"; - else if (re.u.str.is_unit(s)) - print_unit(out, s); else if (re.u.str.is_concat(s)) { expr_ref_vector es(re.m); re.u.str.get_concat(s, es); for (expr* e : es) - print_seq(out, e); + print(out, e); } else if (re.u.str.is_string(s, z)) { for (unsigned i = 0; i < z.length(); i++) out << (char)z[i]; } + else if (re.u.str.is_at(s, x, i)) + print(out, x) << "@", print(out, i); else if (re.u.str.is_extract(s, x, j, k)) { rational jv, iv; print(out, x); @@ -1152,7 +1151,7 @@ std::ostream& seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const { out << "[" << jv.get_int32() << ","; print(out, k); out << "]"; - } + } } else { out << "["; @@ -1162,9 +1161,9 @@ std::ostream& seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const { out << "]"; } } - else - out << mk_pp(s, re.m); - return out; + else + return false; + return true; } /* @@ -1172,9 +1171,9 @@ std::ostream& seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const { */ std::ostream& seq_util::rex::pp::print_range(std::ostream& out, expr* s1, expr* s2) const { out << "["; - print_unit(out, s1); + print(out, s1); out << "-"; - print_unit(out, s2); + print(out, s2); out << "]"; return out; } @@ -1190,9 +1189,10 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const { /* Specialize output for a unit sequence converting to visible ASCII characters if possible. */ -std::ostream& seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const { +bool seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const { expr* e, * i; unsigned n = 0; + if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) { char c = (char)n; if (c == '\n') @@ -1226,24 +1226,18 @@ std::ostream& seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const { out << "\\x" << std::hex << n; else if (n <= 0xFFF) out << "\\u0" << std::hex << n; - else + else out << "\\u" << std::hex << n; } else if (re.u.str.is_nth_i(s, e, i)) { - print(out, e); - out << "[" << mk_pp(i, re.m) << "]"; - } - else if (re.m.is_value(s)) - out << mk_pp(s, re.m); - else if (is_app(s)) { - out << "(" << to_app(s)->get_decl()->get_name().str(); - for (expr * arg : *to_app(s)) - print(out << " ", arg); - out << ")"; + print(out, e) << "["; + print(out, i) << "]"; } + else if (re.u.str.is_length(s, e)) + print(out << "|", e) << "|"; else - out << mk_pp(s, re.m); - return out; + return false; + return true; } /* @@ -1252,17 +1246,20 @@ std::ostream& seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const { std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr; unsigned lo = 0, hi = 0; + arith_util a(re.m); rational v; - if (re.u.is_char(e)) - print_unit(out, e); - else if (re.u.is_seq(e)) - print_seq(out, e); + if (!e) + out << "null"; + else if (print_unit(out, e)) + ; + else if (print_seq(out, e)) + ; else if (re.is_full_char(e)) out << "."; else if (re.is_full_seq(e)) out << ".*"; else if (re.is_to_re(e, s)) - print_seq(out, s); + print(out, s); else if (re.is_range(e, s, s2)) print_range(out, s, s2); else if (re.is_epsilon(e)) @@ -1282,8 +1279,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { print(out, r2); out << ")"; } - else if (re.is_intersection(e, r1, r2)) - { + else if (re.is_intersection(e, r1, r2)) { out << "("; print(out, r1); out << (html_encode ? "⋂" : "&"); @@ -1323,12 +1319,9 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { } } else if (re.is_loop(e, r1, lo)) { - if (can_skip_parenth(r1)) { - print(out, r1); - out << "{" << lo << ",}"; - } - else - { + if (can_skip_parenth(r1)) + print(out, r1) << "{" << lo << ",}"; + else { out << "("; print(out, r1); out << "){" << lo << ",}"; @@ -1368,10 +1361,8 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { out << ")"; } else if (re.is_opt(e, r1)) { - if (can_skip_parenth(r1)) { - print(out, r1); - out << "?"; - } + if (can_skip_parenth(r1)) + print(out, r1) << "?"; else { out << "("; print(out, r1); @@ -1386,7 +1377,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { else if (re.m.is_eq(e, r1, r2)) { out << "("; print(out, r1); - out << "="; + out << " = "; print(out, r2); out << ")"; } @@ -1394,10 +1385,22 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { out << "!"; print(out, r1); } + else if (a.is_add(e, s, s2) && a.is_numeral(s, v) && v < 0) + print(out, s2) << " - " << -v; + else if (a.is_add(e, s, s2) && a.is_numeral(s2, v) && v < 0) + print(out, s) << " - " << -v; + else if (a.is_add(e, s, s2)) + print(out, s) << " + ", print(out, s2); + else if (a.is_sub(e, s, s2) && a.is_numeral(s2, v) && v > 0) + print(out, s) << " - " << v; + else if (a.is_le(e, s, s2)) + print(out << "(", s) << " <= ", print(out, s2) << ")"; else if (re.m.is_value(e)) out << mk_pp(e, re.m); + else if (is_app(e) && to_app(e)->get_num_args() == 0) + out << mk_pp(e, re.m); else if (is_app(e)) { - out << "(" << to_app(e)->get_decl()->get_name().str(); + out << "(" << to_app(e)->get_decl()->get_name(); for (expr* arg : *to_app(e)) print(out << " ", arg); out << ")"; @@ -1409,8 +1412,7 @@ std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const { } std::ostream& seq_util::rex::pp::display(std::ostream& out) const { - print(out, ex); - return out; + return print(out, ex); } /* diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 16edebee9..f194e6a67 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -599,8 +599,8 @@ public: expr* ex; bool html_encode; bool can_skip_parenth(expr* r) const; - std::ostream& print_unit(std::ostream& out, expr* s) const; - std::ostream& print_seq(std::ostream& out, expr* s) const; + bool print_unit(std::ostream& out, expr* s) const; + bool print_seq(std::ostream& out, expr* s) const; std::ostream& print_range(std::ostream& out, expr* s1, expr* s2) const; std::ostream& print(std::ostream& out, expr* e) const; From f78546cd7c4e45ec5eb46416728d6e7dfb54ede1 Mon Sep 17 00:00:00 2001 From: Margus Veanes Date: Fri, 15 Oct 2021 18:02:51 -0700 Subject: [PATCH 34/89] fixed bug of computing butlast of a sequence (#5602) --- src/ast/rewriter/seq_rewriter.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 237cc5d11..f75ccb255 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -936,13 +936,14 @@ expr_ref seq_rewriter::mk_seq_last(expr* t) { } /* -* In general constructs substring(t,0,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j,k-1) -* This method assumes that |t| > 0 holds. +* In general constructs substring(t,0,|t|-1) but if t = substring(s,0,k) then simplifies to substring(s,0,k-1) +* This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct. */ expr_ref seq_rewriter::mk_seq_butlast(expr* t) { expr_ref result(m()); expr* s, * j, * k; - if (str().is_extract(t, s, j, k)) { + rational v; + if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, v) && v.is_zero()) { expr_ref_vector k_min_1(m()); k_min_1.push_back(k); k_min_1.push_back(minus_one()); From 115203e87cb746bead6cd269c7986c05332b3f87 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 16 Oct 2021 15:52:37 -0700 Subject: [PATCH 35/89] fixes to sat.euf ematching #5573 --- src/sat/smt/q_clause.cpp | 6 +- src/sat/smt/q_clause.h | 2 +- src/sat/smt/q_ematch.cpp | 9 +- src/sat/smt/q_mam.cpp | 165 ++++++++++++++++++------------------- src/sat/smt/q_queue.cpp | 9 ++ src/smt/smt_context_pp.cpp | 2 +- 6 files changed, 98 insertions(+), 95 deletions(-) diff --git a/src/sat/smt/q_clause.cpp b/src/sat/smt/q_clause.cpp index 097a61a14..d2b58bb2a 100644 --- a/src/sat/smt/q_clause.cpp +++ b/src/sat/smt/q_clause.cpp @@ -32,8 +32,8 @@ namespace q { << mk_bounded_pp(rhs, m, 2); } - std::ostream& binding::display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const { - for (unsigned i = 0; i < num_nodes; ++i) + std::ostream& binding::display(euf::solver& ctx, std::ostream& out) const { + for (unsigned i = 0; i < size(); ++i) out << ctx.bpp((*this)[i]) << " "; return out; } @@ -46,7 +46,7 @@ namespace q { if (!b) return out; do { - b->display(ctx, num_decls(), out) << "\n"; + b->display(ctx, out) << "\n"; b = b->next(); } while (b != m_bindings); diff --git a/src/sat/smt/q_clause.h b/src/sat/smt/q_clause.h index 08a6f615a..0c89b2ade 100644 --- a/src/sat/smt/q_clause.h +++ b/src/sat/smt/q_clause.h @@ -79,7 +79,7 @@ namespace q { euf::enode* operator[](unsigned i) const { return m_nodes[i]; } - std::ostream& display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const; + std::ostream& display(euf::solver& ctx, std::ostream& out) const; unsigned size() const { return c->num_decls(); } diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index 942c070fd..c0288e5cf 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -270,13 +270,14 @@ namespace q { } void ematch::on_binding(quantifier* q, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_gen, unsigned max_gen) { - TRACE("q", tout << "on-binding " << mk_pp(q, m) << "\n";); unsigned idx = m_q2clauses[q]; clause& c = *m_clauses[idx]; bool new_propagation = false; binding* b = alloc_binding(c, pat, _binding, max_generation, min_gen, max_gen); if (!b) return; + TRACE("q", b->display(ctx, tout << "on-binding " << mk_pp(q, m) << "\n") << "\n";); + if (false && propagate(false, _binding, max_generation, c, new_propagation)) return; @@ -558,7 +559,7 @@ namespace q { m_mam->propagate(); bool propagated = flush_prop_queue(); if (m_qhead >= m_clause_queue.size()) - return m_inst_queue.propagate(); + return m_inst_queue.propagate() || propagated; ctx.push(value_trail(m_qhead)); ptr_buffer to_remove; for (; m_qhead < m_clause_queue.size(); ++m_qhead) { @@ -613,8 +614,8 @@ namespace q { return true; for (unsigned i = 0; i < m_clauses.size(); ++i) if (m_clauses[i]->m_bindings) - std::cout << "missed propagation " << i << "\n"; - + IF_VERBOSE(0, verbose_stream() << "missed propagation " << i << "\n"); + TRACE("q", tout << "no more propagation\n";); return false; } diff --git a/src/sat/smt/q_mam.cpp b/src/sat/smt/q_mam.cpp index 6581d271d..a968314c2 100644 --- a/src/sat/smt/q_mam.cpp +++ b/src/sat/smt/q_mam.cpp @@ -409,16 +409,17 @@ namespace q { unsigned m_num_args; //!< we need this information to avoid the nary *,+ crash bug bool m_filter_candidates; unsigned m_num_regs; - unsigned m_num_choices; - instruction * m_root; + unsigned m_num_choices = 0; + instruction * m_root = nullptr; enode_vector m_candidates; + unsigned m_qhead = 0; #ifdef Z3DEBUG - egraph * m_egraph; + egraph * m_egraph = nullptr; svector> m_patterns; #endif #ifdef _PROFILE_MAM stopwatch m_watch; - unsigned m_counter; + unsigned m_counter = 0; #endif friend class compiler; friend class code_tree_manager; @@ -492,13 +493,7 @@ namespace q { m_root_lbl(lbl), m_num_args(num_args), m_filter_candidates(filter_candidates), - m_num_regs(num_args + 1), - m_num_choices(0), - m_root(nullptr) { - DEBUG_CODE(m_egraph = nullptr;); -#ifdef _PROFILE_MAM - m_counter = 0; -#endif + m_num_regs(num_args + 1) { (void)m_lbl_hasher; } @@ -546,16 +541,40 @@ namespace q { return m_root; } - void add_candidate(enode * n) { + void add_candidate(euf::solver& ctx, enode * n) { m_candidates.push_back(n); + ctx.push(push_back_trail(m_candidates)); } + void unmark(unsigned head) { + for (unsigned i = m_candidates.size(); i-- > head; ) { + enode* app = m_candidates[i]; + if (app->is_marked1()) + app->unmark1(); + } + } + + struct scoped_unmark { + unsigned m_qhead; + code_tree* t; + scoped_unmark(code_tree* t) : m_qhead(t->m_qhead), t(t) {} + ~scoped_unmark() { t->unmark(m_qhead); } + }; + + bool has_candidates() const { - return !m_candidates.empty(); + return m_qhead < m_candidates.size(); } - void reset_candidates() { - m_candidates.reset(); + void save_qhead(euf::solver& ctx) { + ctx.push(value_trail(m_qhead)); + } + + enode* next_candidate() { + if (m_qhead < m_candidates.size()) + return m_candidates[m_qhead++]; + else + return nullptr; } enode_vector const & get_candidates() const { @@ -1987,24 +2006,18 @@ namespace q { m_backtrack_stack.resize(t->get_num_choices()); } - struct scoped_unmark { - code_tree* t; - scoped_unmark(code_tree* t) : t(t) {} - ~scoped_unmark() { - for (enode* app : t->get_candidates()) - if (app->is_marked1()) - app->unmark1(); - } - }; void execute(code_tree * t) { + if (!t->has_candidates()) + return; TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout);); init(t); + t->save_qhead(ctx); + enode* app; if (t->filter_candidates()) { - scoped_unmark _unmark(t); - for (unsigned i = 0; i < t->get_candidates().size() && !ctx.resource_limits_exceeded(); ++i) { - enode* app = t->get_candidates()[i]; - TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_expr(), m) << "\n";); + code_tree::scoped_unmark _unmark(t); + while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) { + TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";); if (!app->is_marked1() && app->is_cgr()) { execute_core(t, app); app->mark1(); @@ -2012,9 +2025,8 @@ namespace q { } } else { - for (unsigned i = 0; i < t->get_candidates().size() && !ctx.resource_limits_exceeded(); ++i) { - enode* app = t->get_candidates()[i]; - TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_expr(), m) << "\n";); + while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) { + TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";); if (app->is_cgr()) execute_core(t, app); } @@ -2477,17 +2489,18 @@ namespace q { case YIELD1: m_bindings[0] = m_registers[static_cast(m_pc)->m_bindings[0]]; -#define ON_MATCH(NUM) \ +#define ON_MATCH(NUM) \ m_max_generation = std::max(m_max_generation, get_max_generation(NUM, m_bindings.begin())); \ - if (!m.inc()) { \ - return false; \ - } \ + if (!m.inc()) \ + return false; \ + \ m_mam.on_match(static_cast(m_pc)->m_qa, \ static_cast(m_pc)->m_pat, \ NUM, \ m_bindings.begin(), \ m_max_generation) + SASSERT(static_cast(m_pc)->m_qa->get_decl_sort(0) == m_bindings[0]->get_expr()->get_sort()); ON_MATCH(1); goto backtrack; @@ -3059,29 +3072,9 @@ namespace q { // temporary field used to collect candidates ptr_vector m_todo; - enode * m_root { nullptr }; // temp field - enode * m_other { nullptr }; // temp field - bool m_check_missing_instances { false }; - - class pop_to_match : public trail { - mam_impl& i; - public: - pop_to_match(mam_impl& i):i(i) {} - void undo() override { - code_tree* t = i.m_to_match.back(); - t->reset_candidates(); - i.m_to_match.pop_back(); - } - }; - - class reset_new_patterns : public trail { - mam_impl& i; - public: - reset_new_patterns(mam_impl& i):i(i) {} - void undo() override { - i.m_new_patterns.reset(); - } - }; + enode * m_root = nullptr; // temp field + enode * m_other = nullptr; // temp field + bool m_check_missing_instances = false; enode_vector * mk_tmp_vector() { enode_vector * r = m_pool.mk(); @@ -3094,14 +3087,14 @@ namespace q { } void add_candidate(code_tree * t, enode * app) { - if (t) { - TRACE("mam_candidate", tout << "adding candidate:\n" << mk_ll_pp(app->get_expr(), m);); - if (!t->has_candidates()) { - ctx.push(pop_to_match(*this)); - m_to_match.push_back(t); - } - t->add_candidate(app); + if (!t) + return; + TRACE("q", tout << "candidate " << t << " " << ctx.bpp(app) << "\n";); + if (!t->has_candidates()) { + ctx.push(push_back_trail(m_to_match)); + m_to_match.push_back(t); } + t->add_candidate(ctx, app); } void add_candidate(enode * app) { @@ -3678,14 +3671,20 @@ namespace q { } } - void match_new_patterns() { + unsigned m_new_patterns_qhead = 0; + + void propagate_new_patterns() { + if (m_new_patterns_qhead >= m_new_patterns.size()) + return; + ctx.push(value_trail(m_new_patterns_qhead)); + TRACE("mam_new_pat", tout << "matching new patterns:\n";); m_tmp_trees_to_delete.reset(); - for (auto const& kv : m_new_patterns) { + for (; m_new_patterns_qhead < m_new_patterns.size(); ++m_new_patterns_qhead) { if (!m.inc()) break; - quantifier * qa = kv.first; - app * mp = kv.second; + auto [qa, mp] = m_new_patterns[m_new_patterns_qhead]; + SASSERT(m.is_pattern(mp)); app * p = to_app(mp->get_arg(0)); func_decl * lbl = p->get_decl(); @@ -3717,7 +3716,6 @@ namespace q { m_tmp_trees[lbl_id] = nullptr; dealloc(tmp_tree); } - m_new_patterns.reset(); } public: @@ -3753,7 +3751,7 @@ namespace q { return; // ignore multi-pattern containing ground pattern. update_filters(qa, mp); m_new_patterns.push_back(qp_pair(qa, mp)); - ctx.push(reset_new_patterns(*this)); + ctx.push(push_back_trail(m_new_patterns)); // The matching abstract machine implements incremental // e-matching. So, for a multi-pattern [ p_1, ..., p_n ], // we have to make n insertions. In the i-th insertion, @@ -3764,7 +3762,6 @@ namespace q { void reset() override { m_trees.reset(); - m_new_patterns.reset(); m_is_plbl.reset(); m_is_clbl.reset(); reset_pp_pc(); @@ -3779,22 +3776,18 @@ namespace q { return out; } - void propagate() override { - TRACE("trigger_bug", tout << "match\n"; display(tout);); - if (m_to_match_head >= m_to_match.size()) + void propagate_to_match() { + if (m_to_match_head >= m_to_match.size()) return; ctx.push(value_trail(m_to_match_head)); - for (; m_to_match_head < m_to_match.size(); ++m_to_match_head) { - code_tree* t = m_to_match[m_to_match_head]; - if (t->has_candidates()) { - m_interpreter.execute(t); - t->reset_candidates(); - } - } - if (!m_new_patterns.empty()) { - match_new_patterns(); - m_new_patterns.reset(); - } + for (; m_to_match_head < m_to_match.size(); ++m_to_match_head) + m_interpreter.execute(m_to_match[m_to_match_head]); + } + + void propagate() override { + TRACE("trigger_bug", tout << "match\n"; display(tout);); + propagate_to_match(); + propagate_new_patterns(); } void rematch(bool use_irrelevant) override { diff --git a/src/sat/smt/q_queue.cpp b/src/sat/smt/q_queue.cpp index c534c9f80..d74ee4e39 100644 --- a/src/sat/smt/q_queue.cpp +++ b/src/sat/smt/q_queue.cpp @@ -149,6 +149,12 @@ namespace q { if (false && em.propagate(true, f.nodes(), gen, *f.c, new_propagation)) return; +#if 0 + std::cout << mk_pp(q, m) << "\n"; + std::cout << num_bindings << "\n"; + for (unsigned i = 0; i < num_bindings; ++i) + std::cout << mk_pp(f[i]->get_expr(), m) << " " << mk_pp(f[i]->get_sort(), m) << "\n"; +#endif auto* ebindings = m_subst(q, num_bindings); for (unsigned i = 0; i < num_bindings; ++i) ebindings[i] = f[i]->get_expr(); @@ -161,6 +167,9 @@ namespace q { stat->inc_num_instances(); m_stats.m_num_instances++; + + // f.display(ctx, std::cout << mk_pp(f.q(), m) << "\n" << instance << "\n") << "\n"; + euf::solver::scoped_generation _sg(ctx, gen); sat::literal result_l = ctx.mk_literal(instance); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 69c773460..a5a42ee7a 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -676,7 +676,7 @@ namespace smt { std::ostream& operator<<(std::ostream& out, enode_pp const& p) { ast_manager& m = p.ctx.get_manager(); enode* n = p.n; - return out << "[#" << n->get_owner_id() << " " << mk_bounded_pp(n->get_expr(), m) << "]"; + return out << n->get_owner_id() << ": " << mk_bounded_pp(n->get_expr(), m); } std::ostream& operator<<(std::ostream& out, enode_eq_pp const& p) { From bc2020a39b9809b2573686ae813318623d3e318e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 17 Oct 2021 20:24:12 -0700 Subject: [PATCH 36/89] #5604 retain array interpretation when available --- src/ast/rewriter/array_rewriter.cpp | 99 +++++++++++++++-------------- src/model/func_interp.cpp | 16 +++-- 2 files changed, 62 insertions(+), 53 deletions(-) diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index c25ae1156..88a0ee28e 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -703,8 +703,60 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) result = m().update_quantifier(lam, quantifier_kind::forall_k, e); return BR_REWRITE2; } - expr_ref lh1(m()), rh1(m()); + + expr_ref_vector fmls(m()); + + + auto has_large_domain = [&](sort* s, unsigned num_stores) { + unsigned sz = get_array_arity(s); + uint64_t dsz = 1; + for (unsigned i = 0; i < sz; ++i) { + sort* d = get_array_domain(s, i); + if (d->is_infinite() || d->is_very_big()) + return true; + auto const& n = d->get_num_elements(); + if (n.size() > num_stores) + return true; + dsz *= n.size(); + if (dsz > num_stores) + return true; + } + return false; + }; + + + if (m_expand_store_eq) { + expr* lhs1 = lhs; + expr* rhs1 = rhs; + unsigned num_lhs = 0, num_rhs = 0; + while (m_util.is_store(lhs1)) { + lhs1 = to_app(lhs1)->get_arg(0); + ++num_lhs; + } + while (m_util.is_store(rhs1)) { + rhs1 = to_app(rhs1)->get_arg(0); + ++num_rhs; + } + if (lhs1 == rhs1) { + mk_eq(lhs, lhs, rhs, fmls); + mk_eq(rhs, lhs, rhs, fmls); + result = m().mk_and(fmls); + return BR_REWRITE_FULL; + } + + if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) && + has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) { + mk_eq(lhs, lhs, rhs, fmls); + mk_eq(rhs, lhs, rhs, fmls); + fmls.push_back(m().mk_eq(v, w)); + result = m().mk_and(fmls); + return BR_REWRITE_FULL; + } + } + + if (m_expand_nested_stores) { + expr_ref lh1(m()), rh1(m()); if (is_expandable_store(lhs)) { lh1 = expand_store(lhs); } @@ -719,10 +771,6 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) } } - if (!m_expand_store_eq) { - return BR_FAILED; - } - expr_ref_vector fmls(m()); #if 0 // lambda friendly version of array equality rewriting. @@ -744,46 +792,5 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) } #endif - expr* lhs1 = lhs; - unsigned num_lhs = 0, num_rhs = 0; - while (m_util.is_store(lhs1)) { - lhs1 = to_app(lhs1)->get_arg(0); - ++num_lhs; - } - expr* rhs1 = rhs; - while (m_util.is_store(rhs1)) { - rhs1 = to_app(rhs1)->get_arg(0); - ++num_rhs; - } - if (lhs1 == rhs1) { - mk_eq(lhs, lhs, rhs, fmls); - mk_eq(rhs, lhs, rhs, fmls); - result = m().mk_and(fmls); - return BR_REWRITE_FULL; - } - auto has_large_domain = [&](sort* s, unsigned num_stores) { - unsigned sz = get_array_arity(s); - uint64_t dsz = 1; - for (unsigned i = 0; i < sz; ++i) { - sort* d = get_array_domain(s, i); - if (d->is_infinite() || d->is_very_big()) - return true; - auto const& n = d->get_num_elements(); - if (n.size() > num_stores) - return true; - dsz *= n.size(); - if (dsz > num_stores) - return true; - } - return false; - }; - if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) && - has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) { - mk_eq(lhs, lhs, rhs, fmls); - mk_eq(rhs, lhs, rhs, fmls); - fmls.push_back(m().mk_eq(v, w)); - result = m().mk_and(fmls); - return BR_REWRITE_FULL; - } return BR_FAILED; } diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 22443ee1f..1c59b6107 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -280,6 +280,9 @@ void func_interp::compress() { } // other compression, if else is a default branch. // or function encode identity. +#if 0 + // breaks array interpretations + // #5604 if (m().is_false(m_else)) { expr_ref new_else(get_interp(), m()); for (func_entry * curr : m_entries) { @@ -291,7 +294,9 @@ void func_interp::compress() { m().dec_ref(m_else); m_else = new_else; } - else if (!m_entries.empty() && is_identity()) { + else +#endif + if (!m_entries.empty() && is_identity()) { for (func_entry * curr : m_entries) { curr->deallocate(m(), m_arity); } @@ -335,14 +340,11 @@ expr * func_interp::get_interp_core() const { expr * r = m_else; ptr_buffer vars; for (func_entry * curr : m_entries) { - if (m_else == curr->get_result()) { + if (m_else == curr->get_result()) continue; - } - if (vars.empty()) { - for (unsigned i = 0; i < m_arity; i++) { + if (vars.empty()) + for (unsigned i = 0; i < m_arity; i++) vars.push_back(m().mk_var(i, curr->get_arg(i)->get_sort())); - } - } ptr_buffer eqs; for (unsigned i = 0; i < m_arity; i++) { eqs.push_back(m().mk_eq(vars[i], curr->get_arg(i))); From d5e5dcfe451a2a2b2ba2097df66881c878f46a0f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Oct 2021 15:32:55 -0700 Subject: [PATCH 37/89] add nff and auto-relevant Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_relevancy.cpp | 44 +++++++++++++++++++++++++++++------ src/sat/smt/euf_solver.cpp | 1 + src/sat/smt/euf_solver.h | 13 +++++++++-- src/sat/smt/q_ematch.cpp | 27 +++++++++++++++++---- src/sat/smt/q_ematch.h | 8 +++++++ 5 files changed, 80 insertions(+), 13 deletions(-) diff --git a/src/sat/smt/euf_relevancy.cpp b/src/sat/smt/euf_relevancy.cpp index 28ac99d9a..d7f7b9012 100644 --- a/src/sat/smt/euf_relevancy.cpp +++ b/src/sat/smt/euf_relevancy.cpp @@ -22,6 +22,31 @@ Author: namespace euf { + void solver::add_auto_relevant(expr* e) { + if (!relevancy_enabled()) + return; + for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes) + m_auto_relevant_lim.push_back(m_auto_relevant.size()); + m_auto_relevant.push_back(e); + } + + void solver::pop_relevant(unsigned n) { + if (m_auto_relevant_scopes >= n) { + m_auto_relevant_scopes -= n; + return; + } + n -= m_auto_relevant_scopes; + m_auto_relevant_scopes = 0; + unsigned top = m_auto_relevant_lim.size() - n; + unsigned lim = m_auto_relevant_lim[top]; + m_auto_relevant_lim.shrink(top); + m_auto_relevant.shrink(lim); + } + + void solver::push_relevant() { + ++m_auto_relevant_scopes; + } + bool solver::is_relevant(expr* e) const { return m_relevant_expr_ids.get(e->get_id(), true); } @@ -31,11 +56,11 @@ namespace euf { } void solver::ensure_dual_solver() { - if (!m_dual_solver) { - m_dual_solver = alloc(sat::dual_solver, s().rlimit()); - for (unsigned i = s().num_user_scopes(); i-- > 0; ) - m_dual_solver->push(); - } + if (m_dual_solver) + return; + m_dual_solver = alloc(sat::dual_solver, s().rlimit()); + for (unsigned i = s().num_user_scopes(); i-- > 0; ) + m_dual_solver->push(); } /** @@ -65,8 +90,6 @@ namespace euf { bool solver::init_relevancy() { m_relevant_expr_ids.reset(); - bool_vector visited; - ptr_vector todo; if (!relevancy_enabled()) return true; if (!m_dual_solver) @@ -77,12 +100,16 @@ namespace euf { for (enode* n : m_egraph.nodes()) max_id = std::max(max_id, n->get_expr_id()); m_relevant_expr_ids.resize(max_id + 1, false); + ptr_vector& todo = m_relevant_todo; + bool_vector& visited = m_relevant_visited; auto const& core = m_dual_solver->core(); + todo.reset(); for (auto lit : core) { expr* e = m_bool_var2expr.get(lit.var(), nullptr); if (e) todo.push_back(e); } + todo.append(m_auto_relevant); for (unsigned i = 0; i < todo.size(); ++i) { expr* e = todo[i]; if (visited.get(e->get_id(), false)) @@ -114,6 +141,9 @@ namespace euf { todo.push_back(arg); } + for (auto * e : todo) + visited[e->get_id()] = false; + TRACE("euf", for (enode* n : m_egraph.nodes()) if (is_relevant(n)) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 3c1831c87..0bbce0993 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -183,6 +183,7 @@ namespace euf { } void solver::propagate(literal lit, ext_justification_idx idx) { + add_auto_relevant(bool_var2expr(lit.var())); s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx)); } diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 2449335d1..0df07faff 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -99,8 +99,7 @@ namespace euf { sat::lookahead* m_lookahead = nullptr; ast_manager* m_to_m; sat::sat_internalizer* m_to_si; - scoped_ptr m_ackerman; - scoped_ptr m_dual_solver; + scoped_ptr m_ackerman; user_solver::solver* m_user_propagator = nullptr; th_solver* m_qsolver = nullptr; unsigned m_generation = 0; @@ -182,6 +181,8 @@ namespace euf { // relevancy bool_vector m_relevant_expr_ids; + bool_vector m_relevant_visited; + ptr_vector m_relevant_todo; void ensure_dual_solver(); bool init_relevancy(); @@ -363,6 +364,11 @@ namespace euf { // relevancy bool m_relevancy = true; + scoped_ptr m_dual_solver; + ptr_vector m_auto_relevant; + unsigned_vector m_auto_relevant_lim; + unsigned m_auto_relevant_scopes = 0; + bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; } void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false; } void add_root(unsigned n, sat::literal const* lits); @@ -377,6 +383,9 @@ namespace euf { void track_relevancy(sat::bool_var v); bool is_relevant(expr* e) const; bool is_relevant(enode* n) const; + void add_auto_relevant(expr* e); + void pop_relevant(unsigned n); + void push_relevant(); // model construction diff --git a/src/sat/smt/q_ematch.cpp b/src/sat/smt/q_ematch.cpp index c0288e5cf..e4e01b964 100644 --- a/src/sat/smt/q_ematch.cpp +++ b/src/sat/smt/q_ematch.cpp @@ -54,7 +54,11 @@ namespace q { m_eval(ctx), m_qstat_gen(m, ctx.get_region()), m_inst_queue(*this, ctx), - m_infer_patterns(m, ctx.get_config()) + m_infer_patterns(m, ctx.get_config()), + m_new_defs(m), + m_new_proofs(m), + m_dn(m), + m_nnf(m, m_dn) { std::function _on_merge = [&](euf::enode* root, euf::enode* other) { @@ -105,6 +109,20 @@ namespace q { m_eval.explain(l, justification::from_index(idx), r, probing); } + quantifier_ref ematch::nnf_skolem(quantifier* q) { + expr_ref r(m); + proof_ref p(m); + m_new_defs.reset(); + m_new_proofs.reset(); + m_nnf(q, m_new_defs, m_new_proofs, r, p); + SASSERT(is_quantifier(r)); + for (expr* d : m_new_defs) + m_qs.add_unit(m_qs.mk_literal(d)); + CTRACE("q", r != q, tout << mk_pp(q, m) << " -->\n" << r << "\n" << m_new_defs << "\n";); + return quantifier_ref(to_quantifier(r), m); + } + + std::ostream& ematch::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { auto& j = justification::from_index(idx); auto& c = j.m_clause; @@ -218,8 +236,6 @@ namespace q { } }; - - binding* ematch::tmp_binding(clause& c, app* pat, euf::enode* const* b) { if (c.num_decls() > m_tmp_binding_capacity) { void* mem = memory::allocate(sizeof(binding) + c.num_decls() * sizeof(euf::enode*)); @@ -430,7 +446,10 @@ namespace q { cl->m_literal.neg(); expr_ref body(mk_not(m, q->get_expr()), m); q = m.update_quantifier(q, forall_k, body); - } + } + q = nnf_skolem(q); + + expr_ref_vector ors(m); flatten_or(q->get_expr(), ors); for (expr* arg : ors) diff --git a/src/sat/smt/q_ematch.h b/src/sat/smt/q_ematch.h index 58037e308..3cdcfc80e 100644 --- a/src/sat/smt/q_ematch.h +++ b/src/sat/smt/q_ematch.h @@ -19,6 +19,7 @@ Author: #include "util/nat_set.h" #include "ast/quantifier_stat.h" #include "ast/pattern/pattern_inference.h" +#include "ast/normal_forms/nnf.h" #include "solver/solver.h" #include "sat/smt/sat_th.h" #include "sat/smt/q_mam.h" @@ -119,6 +120,13 @@ namespace q { bool propagate(bool flush); + expr_ref_vector m_new_defs; + proof_ref_vector m_new_proofs; + defined_names m_dn; + nnf m_nnf; + + quantifier_ref nnf_skolem(quantifier* q); + public: ematch(euf::solver& ctx, solver& s); From fc3a7018888c1f507d191d6c4799ea966bd8de70 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Oct 2021 15:36:48 -0700 Subject: [PATCH 38/89] push-pop Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_solver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 0bbce0993..2af8485c1 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -528,6 +528,7 @@ namespace euf { m_egraph.push(); if (m_dual_solver) m_dual_solver->push(); + push_relevant(); } void solver::pop(unsigned n) { @@ -537,6 +538,7 @@ namespace euf { e->pop(n); si.pop(n); m_egraph.pop(n); + pop_relevant(n); scope const & sc = m_scopes[m_scopes.size() - n]; for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) { bool_var v = m_var_trail[i]; From 3557e0b0c52691c08836dfbc2bd37c2def221646 Mon Sep 17 00:00:00 2001 From: CEisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Tue, 19 Oct 2021 16:48:31 +0200 Subject: [PATCH 39/89] Added eq/fixed/final functions in C++ user propagator as methods (#5607) --- src/api/c++/z3++.h | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f0c0cc3aa..b855b30cc 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1516,7 +1516,7 @@ namespace z3 { expr substitute(expr_vector const& dst); - class iterator { + class iterator { expr& e; unsigned i; public: @@ -1912,14 +1912,14 @@ namespace z3 { Z3_ast r; if (a.is_int()) { expr zero = a.ctx().int_val(0); - expr ge = a >= zero; - expr na = -a; + expr ge = a >= zero; + expr na = -a; r = Z3_mk_ite(a.ctx(), ge, a, na); } else if (a.is_real()) { expr zero = a.ctx().real_val(0); - expr ge = a >= zero; - expr na = -a; + expr ge = a >= zero; + expr na = -a; r = Z3_mk_ite(a.ctx(), ge, a, na); } else { @@ -3954,12 +3954,28 @@ namespace z3 { Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); } + void register_fixed() { + assert(s); + m_fixed_eh = [this](unsigned id, expr const& e) { + fixed(id, e); + }; + Z3_solver_propagate_fixed(ctx(), *s, fixed_eh); + } + void register_eq(eq_eh_t& f) { assert(s); m_eq_eh = f; Z3_solver_propagate_eq(ctx(), *s, eq_eh); } + void register_eq() { + assert(s); + m_eq_eh = [this](unsigned x, unsigned y) { + eq(x, y); + }; + Z3_solver_propagate_eq(ctx(), *s, eq_eh); + } + /** \brief register a callback on final-check. During the final check stage, all propagations have been processed. @@ -3973,6 +3989,21 @@ namespace z3 { m_final_eh = f; Z3_solver_propagate_final(ctx(), *s, final_eh); } + + void register_final() { + assert(s); + m_final_eh = [this]() { + final(); + }; + Z3_solver_propagate_final(ctx(), *s, final_eh); + } + + + virtual void fixed(unsigned id, expr const& e) { } + + virtual void eq(unsigned x, unsigned y) { } + + virtual void final() { } /** \brief tracks \c e by a unique identifier that is returned by the call. From f9dde2e8a4709ba37341dc12a9b4a3d543769668 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Oct 2021 12:21:54 -0400 Subject: [PATCH 40/89] #5605 Signed-off-by: Nikolaj Bjorner --- src/opt/maxlex.cpp | 9 +++++---- src/opt/opt_context.cpp | 3 ++- src/sat/smt/euf_relevancy.cpp | 4 ++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/opt/maxlex.cpp b/src/opt/maxlex.cpp index a545aa92e..46c7104d5 100644 --- a/src/opt/maxlex.cpp +++ b/src/opt/maxlex.cpp @@ -135,8 +135,10 @@ namespace opt { if (mdl) { TRACE("opt", tout << *mdl << "\n";); for (auto & soft : m_soft) { - if (!mdl->is_true(soft.s)) - break; + if (!mdl->is_true(soft.s)) { + update_bounds(); + return; + } soft.set_value(l_true); assert_value(soft); } @@ -151,9 +153,8 @@ namespace opt { unsigned sz = m_soft.size(); for (unsigned i = 0; i < sz; ++i) { auto& soft = m_soft[i]; - if (soft.value != l_undef) { + if (soft.value != l_undef) continue; - } expr_ref_vector asms(m); asms.push_back(soft.s); lbool is_sat = s().check_sat(asms); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 219b5488d..e78da961b 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -24,6 +24,7 @@ Notes: #include "ast/pb_decl_plugin.h" #include "ast/ast_smt_pp.h" #include "ast/ast_pp_util.h" +#include "ast/ast_ll_pp.h" #include "ast/display_dimacs.h" #include "model/model_smt2_pp.h" #include "tactic/goal.h" @@ -1200,7 +1201,7 @@ namespace opt { app* context::purify(generic_model_converter_ref& fm, expr* term) { std::ostringstream out; - out << mk_pp(term, m); + out << mk_bounded_pp(term, m, 3); app* q = m.mk_fresh_const(out.str(), term->get_sort()); if (!fm) fm = alloc(generic_model_converter, m, "opt"); if (m_arith.is_int_real(term)) { diff --git a/src/sat/smt/euf_relevancy.cpp b/src/sat/smt/euf_relevancy.cpp index d7f7b9012..06c0469ea 100644 --- a/src/sat/smt/euf_relevancy.cpp +++ b/src/sat/smt/euf_relevancy.cpp @@ -27,6 +27,7 @@ namespace euf { return; for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes) m_auto_relevant_lim.push_back(m_auto_relevant.size()); + std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; m_auto_relevant.push_back(e); } @@ -109,6 +110,9 @@ namespace euf { if (e) todo.push_back(e); } + std::cout << "init-relevant\n"; + for (expr* e : m_auto_relevant) + std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; todo.append(m_auto_relevant); for (unsigned i = 0; i < todo.size(); ++i) { expr* e = todo[i]; From 86147d01ea5c2c1b288e37dbb3ffd5c218cddf1c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 19 Oct 2021 12:24:29 -0400 Subject: [PATCH 41/89] #5605 Signed-off-by: Nikolaj Bjorner --- src/sat/smt/euf_relevancy.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sat/smt/euf_relevancy.cpp b/src/sat/smt/euf_relevancy.cpp index 06c0469ea..40dff962a 100644 --- a/src/sat/smt/euf_relevancy.cpp +++ b/src/sat/smt/euf_relevancy.cpp @@ -27,7 +27,7 @@ namespace euf { return; for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes) m_auto_relevant_lim.push_back(m_auto_relevant.size()); - std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; + // std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; m_auto_relevant.push_back(e); } @@ -110,9 +110,11 @@ namespace euf { if (e) todo.push_back(e); } +#if 0 std::cout << "init-relevant\n"; for (expr* e : m_auto_relevant) std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; +#endif todo.append(m_auto_relevant); for (unsigned i = 0; i < todo.size(); ++i) { expr* e = todo[i]; From 13da6a02a60282de54cfa8790fcc10cf22f6d263 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Oct 2021 12:27:56 -0400 Subject: [PATCH 42/89] add handling of quantifiers #5612 --- src/smt/smt_context.h | 3 ++- src/smt/smt_enode.cpp | 2 +- src/smt/theory_datatype.cpp | 2 +- src/smt/theory_recfun.cpp | 39 ++++++++++++++++++++++--------------- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index d8eea758f..b5a19569a 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1192,7 +1192,6 @@ namespace smt { bool more_than_k_unassigned_literals(clause * cls, unsigned k); - void internalize_assertions(); void asserted_inconsistent(); @@ -1609,6 +1608,8 @@ namespace smt { void assert_expr(expr * e, proof * pr); + void internalize_assertions(); + void push(); void pop(unsigned num_scopes); diff --git a/src/smt/smt_enode.cpp b/src/smt/smt_enode.cpp index a54a287ec..49f05b019 100644 --- a/src/smt/smt_enode.cpp +++ b/src/smt/smt_enode.cpp @@ -50,7 +50,7 @@ namespace smt { n->m_lbl_hash = -1; n->m_proof_is_logged = false; unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { + for (unsigned i = 0; i < num_args; i++) { enode * arg = app2enode[owner->get_arg(i)->get_id()]; n->m_args[i] = arg; SASSERT(n->get_arg(i) == arg); diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index 80b3b5062..765513823 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -297,7 +297,7 @@ namespace smt { TRACE("datatype", tout << "internalizing term:\n" << mk_pp(term, m) << "\n";); unsigned num_args = term->get_num_args(); for (unsigned i = 0; i < num_args; i++) - ctx.internalize(term->get_arg(i), false); + ctx.internalize(term->get_arg(i), has_quantifiers(term)); // the internalization of the arguments may trigger the internalization of term. if (ctx.e_internalized(term)) return true; diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index e21eb2bc5..1a2bf0049 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -246,18 +246,22 @@ namespace smt { literal theory_recfun::mk_eq_lit(expr* l, expr* r) { literal lit; - if (m.is_true(r) || m.is_false(r)) { - std::swap(l, r); - } - if (m.is_true(l)) { - lit = mk_literal(r); - } - else if (m.is_false(l)) { - lit = ~mk_literal(r); - } - else { - lit = mk_eq(l, r, false); + if (has_quantifiers(l) || has_quantifiers(r)) { + expr_ref eq1(m.mk_eq(l, r), m); + expr_ref fn(m.mk_fresh_const("rec-eq", m.mk_bool_sort()), m); + expr_ref eq(m.mk_eq(fn, eq1), m); + ctx.assert_expr(eq); + ctx.internalize_assertions(); + lit = mk_literal(fn); } + else if (m.is_true(r) || m.is_false(r)) + std::swap(l, r); + else if (m.is_true(l)) + lit = mk_literal(r); + else if (m.is_false(l)) + lit = ~mk_literal(r); + else + lit = mk_eq(l, r, false); ctx.mark_as_relevant(lit); return lit; } @@ -282,14 +286,12 @@ namespace smt { auto & vars = e.m_def->get_vars(); expr_ref lhs(e.m_lhs, m); unsigned depth = get_depth(e.m_lhs); - expr_ref rhs(apply_args(depth, vars, e.m_args, e.m_def->get_rhs()), m); + expr_ref rhs(apply_args(depth, vars, e.m_args, e.m_def->get_rhs()), m); literal lit = mk_eq_lit(lhs, rhs); std::function fn = [&]() { return lit; }; scoped_trace_stream _tr(*this, fn); ctx.mk_th_axiom(get_id(), 1, &lit); TRACEFN("macro expansion yields " << pp_lit(ctx, lit)); - if (has_quantifiers(rhs)) - throw default_exception("quantified formulas in recursive functions are not supported"); } /** @@ -377,6 +379,13 @@ namespace smt { unsigned depth = get_depth(e.m_pred); expr_ref lhs(u().mk_fun_defined(d, args), m); expr_ref rhs = apply_args(depth, vars, args, e.m_cdef->get_rhs()); + if (has_quantifiers(rhs)) { + expr_ref fn(m.mk_fresh_const("rec-eq", m.mk_bool_sort()), m); + expr_ref eq(m.mk_eq(fn, rhs), m); + ctx.assert_expr(eq); + ctx.internalize_assertions(); + rhs = fn; + } literal_vector clause; for (auto & g : e.m_cdef->get_guards()) { expr_ref guard = apply_args(depth, vars, args, g); @@ -394,8 +403,6 @@ namespace smt { std::function fn = [&]() { return clause; }; scoped_trace_stream _tr(*this, fn); ctx.mk_th_axiom(get_id(), clause); - if (has_quantifiers(rhs)) - throw default_exception("quantified formulas in recursive functions are not supported"); } final_check_status theory_recfun::final_check_eh() { From 6eed885379370a1ec1912cef7e08b9504e37116b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Oct 2021 10:42:39 -0400 Subject: [PATCH 43/89] print bounded terms for better efficiency --- src/opt/opt_context.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index e78da961b..a8c0d5144 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -967,12 +967,12 @@ namespace opt { tout << "Convert minimization " << orig_term << "\n"; tout << "to maxsat: " << term << "\n"; for (unsigned i = 0; i < weights.size(); ++i) { - tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n"; + tout << mk_pp(terms.get(i), m) << ": " << weights[i] << "\n"; } tout << "offset: " << offset << "\n"; ); std::ostringstream out; - out << orig_term << ':' << index; + out << mk_bounded_pp(orig_term, m, 2) << ':' << index; id = symbol(out.str()); return true; } @@ -995,7 +995,7 @@ namespace opt { } neg = true; std::ostringstream out; - out << orig_term << ':' << index; + out << mk_bounded_pp(orig_term, m) << ':' << index; id = symbol(out.str()); return true; } @@ -1014,7 +1014,7 @@ namespace opt { } neg = is_max; std::ostringstream out; - out << orig_term << ':' << index; + out << mk_bounded_pp(orig_term, m, 2) << ':' << index; id = symbol(out.str()); return true; } From 05e7ed9637a7dc6eeb435094beaf2f386598c722 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Oct 2021 11:30:03 -0400 Subject: [PATCH 44/89] add API to access unescaped strings, update documentation of Z3_get_lstring, #5615 Signed-off-by: Nikolaj Bjorner --- src/api/api_seq.cpp | 32 ++++++++++++++++++++++++++++++++ src/api/z3_api.h | 22 +++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index f0f1d903e..d2cf391e1 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -215,6 +215,38 @@ extern "C" { Z3_CATCH_RETURN(""); } + unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s) { + Z3_TRY; + LOG_Z3_get_string_length(c, s); + RESET_ERROR_CODE(); + zstring str; + if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) { + SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal"); + } + return str.length(); + Z3_CATCH_RETURN(0); + } + + void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned* contents) { + Z3_TRY; + LOG_Z3_get_string_contents(c, s, length, contents); + RESET_ERROR_CODE(); + zstring str; + if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) { + SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal"); + return; + } + if (str.length() != length) { + SET_ERROR_CODE(Z3_INVALID_ARG, "string size disagrees with supplied buffer length"); + return; + } + for (unsigned i = 0; i < length; ++i) + contents[i] = str[i]; + + Z3_CATCH; + } + + #define MK_SORTED(NAME, FN ) \ Z3_ast Z3_API NAME(Z3_context c, Z3_sort s) { \ Z3_TRY; \ diff --git a/src/api/z3_api.h b/src/api/z3_api.h index a15e77788..77a40c2fd 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3510,14 +3510,34 @@ extern "C" { Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s); /** - \brief Retrieve the unescaped string constant stored in \c s. + \brief Retrieve the string constant stored in \c s. The string can contain escape sequences. \pre Z3_is_string(c, s) def_API('Z3_get_lstring', CHAR_PTR, (_in(CONTEXT), _in(AST), _out(UINT))) */ Z3_char_ptr Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length); + + /** + \brief Retrieve the length of the unescaped string constant stored in \c s. + \pre Z3_is_string(c, s) + + def_API('Z3_get_string_length', UINT, (_in(CONTEXT), _in(AST))) + */ + unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s); + + /** + \brief Retrieve the unescaped string constant stored in \c s. + + \pre Z3_is_string(c, s) + + \pre length contains the number of characters in s + + def_API('Z3_get_string_contents', VOID, (_in(CONTEXT), _in(AST), _in(UINT), _out_array(2, UINT))) + */ + void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned* buffer); + /** \brief Create an empty sequence of the sequence sort \c seq. From f05ac8a42950e6d1406f3394c5277d40aa21bb99 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Oct 2021 14:52:59 -0400 Subject: [PATCH 45/89] updated C++ API for escaped and unescaped strings #5615 Signed-off-by: Nikolaj Bjorner --- examples/c++/example.cpp | 4 ++++ src/api/api_seq.cpp | 3 +-- src/api/c++/z3++.h | 15 ++++++++------- src/smt/user_propagator.h | 8 ++++---- src/solver/solver.h | 2 +- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 9ae621dbe..7f4a4e334 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -1249,10 +1249,14 @@ void recfun_example() { static void string_values() { context c; + std::cout << "string_values\n"; expr s = c.string_val("abc\n\n\0\0", 7); std::cout << s << "\n"; std::string s1 = s.get_string(); std::cout << s1 << "\n"; + std::vector buffer = s.get_wstring(); + for (unsigned ch : buffer) + std::cout << "char: " << ch << "\n"; } expr MakeStringConstant(context* context, std::string value) { diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index d2cf391e1..06572756d 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -187,10 +187,9 @@ extern "C" { svector buff; for (unsigned i = 0; i < str.length(); ++i) { unsigned ch = str[i]; - if (ch <= 32 || ch >= 127) { + if (ch <= 32 || ch >= 127 || (ch == '\\' && i + 1 < str.length() && str[i+1] == 'u')) { buff.reset(); buffer.push_back('\\'); -// buffer.push_back('\\'); // possibly replace by native non-escaped version? buffer.push_back('u'); buffer.push_back('{'); while (ch > 0) { diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index b855b30cc..9ebd5e6b1 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1100,23 +1100,24 @@ namespace z3 { bool is_string_value() const { return Z3_is_string(ctx(), m_ast); } /** - \brief for a string value expression return an escaped or unescaped string value. + \brief for a string value expression return an escaped string value. \pre expression is for a string value. */ - std::string get_escaped_string() const { + std::string get_string() const { assert(is_string_value()); char const* s = Z3_get_string(ctx(), m_ast); check_error(); return std::string(s); } - std::string get_string() const { + std::vector get_wstring() const { assert(is_string_value()); - unsigned n; - char const* s = Z3_get_lstring(ctx(), m_ast, &n); - check_error(); - return std::string(s, n); + unsigned n = Z3_get_string_length(ctx(), m_ast); + std::vector buffer; + buffer.resize(n); + Z3_get_string_contents(ctx(), m_ast, n, buffer.data()); + return buffer; } operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } diff --git a/src/smt/user_propagator.h b/src/smt/user_propagator.h index e37dd751d..dcf41c3bb 100644 --- a/src/smt/user_propagator.h +++ b/src/smt/user_propagator.h @@ -48,7 +48,7 @@ namespace smt { void reset() { memset(this, 0, sizeof(*this)); } }; - void* m_user_context; + void* m_user_context = nullptr; solver::push_eh_t m_push_eh; solver::pop_eh_t m_pop_eh; solver::fresh_eh_t m_fresh_eh; @@ -56,13 +56,13 @@ namespace smt { solver::fixed_eh_t m_fixed_eh; solver::eq_eh_t m_eq_eh; solver::eq_eh_t m_diseq_eh; - solver::context_obj* m_api_context { nullptr }; - unsigned m_qhead { 0 }; + solver::context_obj* m_api_context = nullptr; + unsigned m_qhead = 0; uint_set m_fixed; vector m_prop; unsigned_vector m_prop_lim; vector m_id2justification; - unsigned m_num_scopes { 0 }; + unsigned m_num_scopes = 0; literal_vector m_lits; enode_pair_vector m_eqs; stats m_stats; diff --git a/src/solver/solver.h b/src/solver/solver.h index 14fd49bce..a6d6f8169 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -51,7 +51,7 @@ class solver : public check_sat_result { params_ref m_params; symbol m_cancel_backup_file; public: - solver() {} + solver() {} ~solver() override {} /** From cd8d8bbb630dc8db8c01b8ec9987ca6640c42720 Mon Sep 17 00:00:00 2001 From: Weng Shiwei Date: Thu, 21 Oct 2021 18:27:54 -0400 Subject: [PATCH 46/89] Fix runtime search path for shared-lib and add '-static' to the name of static-lib. (#5616) * Fix runtime search path for shared-lib and add '-static' to static-lib. * Revert the change on `META.in`. --- scripts/mk_util.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 2b4b6179e..baa00b191 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2013,6 +2013,9 @@ class MLComponent(Component): LIBZ3 = z3linkdep LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split())) + if not STATIC_LIB: + dllpath = '-dllpath $$(%s printconf path)/stublibs' % OCAMLFIND + LIBZ3 = LIBZ3 + ' ' + dllpath if DEBUG_MODE and not(is_cygwin()): # Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well. @@ -2020,10 +2023,14 @@ class MLComponent(Component): z3mls = os.path.join(self.sub_dir, 'z3ml') + LIBZ3ML = '' + if STATIC_LIB: + LIBZ3ML = '-oc ' + os.path.join(self.sub_dir, 'z3ml-static') + out.write('%s.cma: %s %s %s\n' % (z3mls, cmos, stubso, z3linkdep)) - out.write('\t%s -o %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmos, LIBZ3)) + out.write('\t%s -o %s %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, LIBZ3ML, self.sub_dir, stubso, cmos, LIBZ3)) out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3linkdep, z3mls)) - out.write('\t%s -o %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, self.sub_dir, stubso, cmxs, LIBZ3)) + out.write('\t%s -o %s %s -I %s -L. %s %s %s\n' % (OCAMLMKLIB, z3mls, LIBZ3ML, self.sub_dir, stubso, cmxs, LIBZ3)) out.write('%s.cmxs: %s.cmxa\n' % (z3mls, z3mls)) out.write('\t%s -linkall -shared -o %s.cmxs -I . -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls)) @@ -2041,6 +2048,7 @@ class MLComponent(Component): self.mk_uninstall(out) out.write('\n') + # The following three functions may be out of date. def mk_install_deps(self, out): if is_ml_enabled() and self._install_bindings(): out.write(get_component(Z3_DLL_COMPONENT).dll_name + '$(SO_EXT) ') From 051616385f598196ad0b3d080086a5cba14c2fdd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Oct 2021 19:14:04 -0400 Subject: [PATCH 47/89] remove deprecated escape string from Julia bindings Signed-off-by: Nikolaj Bjorner --- src/api/julia/z3jl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api/julia/z3jl.cpp b/src/api/julia/z3jl.cpp index ccb2f7d07..73e4356b2 100644 --- a/src/api/julia/z3jl.cpp +++ b/src/api/julia/z3jl.cpp @@ -214,7 +214,6 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m) .MM(expr, numerator) .MM(expr, denominator) .MM(expr, is_string_value) - .MM(expr, get_escaped_string) .MM(expr, get_string) .MM(expr, decl) .MM(expr, num_args) From 7f41d6140f6b278b132739f492e62ff4c68e3dff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Oct 2021 12:39:55 -0400 Subject: [PATCH 48/89] use some suggestions from #5615 Signed-off-by: Nikolaj Bjorner --- examples/c++/example.cpp | 2 +- src/api/api_seq.cpp | 13 ++++++++++++- src/api/c++/z3++.h | 14 ++++++++++++-- src/api/z3_api.h | 9 +++++++++ src/smt/smt_parallel.cpp | 11 ++++------- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 7f4a4e334..ff2470ba7 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -1254,7 +1254,7 @@ static void string_values() { std::cout << s << "\n"; std::string s1 = s.get_string(); std::cout << s1 << "\n"; - std::vector buffer = s.get_wstring(); + std::u32string buffer = s.get_u32string(); for (unsigned ch : buffer) std::cout << "char: " << ch << "\n"; } diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 06572756d..ca7c860db 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -58,7 +58,7 @@ extern "C" { Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) { Z3_TRY; - LOG_Z3_mk_string(c, str); + LOG_Z3_mk_lstring(c, sz, str); RESET_ERROR_CODE(); unsigned_vector chs; for (unsigned i = 0; i < sz; ++i) chs.push_back((unsigned char)str[i]); @@ -69,6 +69,17 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned sz, unsigned const chars[]) { + Z3_TRY; + LOG_Z3_mk_u32string(c, sz, chars); + RESET_ERROR_CODE(); + zstring s(sz, chars); + app* a = mk_c(c)->sutil().str.mk_string(s); + mk_c(c)->save_ast_trail(a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(nullptr); + } + Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_string_sort(c); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 9ebd5e6b1..f81bcb6f8 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -379,6 +379,7 @@ namespace z3 { expr string_val(char const* s); expr string_val(char const* s, unsigned n); expr string_val(std::string const& s); + expr string_val(std::u32string const& s); expr num_val(int n, sort const & s); @@ -1111,13 +1112,21 @@ namespace z3 { return std::string(s); } - std::vector get_wstring() const { + /** + \brief for a string value expression return an unespaced string value. + \pre expression is for a string value. + */ + + std::u32string get_u32string() const { assert(is_string_value()); unsigned n = Z3_get_string_length(ctx(), m_ast); std::vector buffer; buffer.resize(n); Z3_get_string_contents(ctx(), m_ast, n, buffer.data()); - return buffer; + std::u32string s; + for (auto ch : buffer) + s.push_back(ch); + return s; } operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } @@ -3481,6 +3490,7 @@ namespace z3 { inline expr context::string_val(char const* s, unsigned n) { Z3_ast r = Z3_mk_lstring(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); } inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); } + inline expr context::string_val(std::u32string const& s) { Z3_ast r = Z3_mk_u32string(m_ctx, (unsigned)s.size(), (unsigned const*)s.c_str()); check_error(); return expr(*this, r); } inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 77a40c2fd..7d252fd26 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3493,6 +3493,15 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned len, Z3_string s); + /** + \brief Create a string constant out of the string that is passed in + It takes the length of the string as well to take into account + 0 characters. The string is unescaped. + + def_API('Z3_mk_u32string', AST, (_in(CONTEXT), _in(UINT), _in_array(1, UINT))) + */ + Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned len, unsigned const chars[]); + /** \brief Determine if \c s is a string constant. diff --git a/src/smt/smt_parallel.cpp b/src/smt/smt_parallel.cpp index 744d5ce2b..82bda1d39 100644 --- a/src/smt/smt_parallel.cpp +++ b/src/smt/smt_parallel.cpp @@ -149,21 +149,18 @@ namespace smt { expr_ref c(pm); pctx.get_fparams().m_max_conflicts = std::min(thread_max_conflicts, max_conflicts); - if (num_rounds > 0 && (pctx.get_fparams().m_threads_cube_frequency % num_rounds) == 0) { + if (num_rounds > 0 && (pctx.get_fparams().m_threads_cube_frequency % num_rounds) == 0) cube(pctx, lasms, c); - } IF_VERBOSE(1, verbose_stream() << "(smt.thread " << i; if (num_rounds > 0) verbose_stream() << " :round " << num_rounds; if (c) verbose_stream() << " :cube " << mk_bounded_pp(c, pm, 3); verbose_stream() << ")\n";); lbool r = pctx.check(lasms.size(), lasms.data()); - if (r == l_undef && pctx.m_num_conflicts >= max_conflicts) { - // no-op - } - else if (r == l_undef && pctx.m_num_conflicts >= thread_max_conflicts) { + if (r == l_undef && pctx.m_num_conflicts >= max_conflicts) + ; // no-op + else if (r == l_undef && pctx.m_num_conflicts >= thread_max_conflicts) return; - } else if (r == l_false && pctx.unsat_core().contains(c)) { IF_VERBOSE(1, verbose_stream() << "(smt.thread " << i << " :learn " << mk_bounded_pp(c, pm, 3) << ")"); pctx.assert_expr(mk_not(mk_and(pctx.unsat_core()))); From 3a3cef8fcef58f31e9ec6495346eb065b816b155 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Oct 2021 18:21:51 +0200 Subject: [PATCH 49/89] #5615 - update documentation and use non-encoded versions for ASCII characters in get_lstring Signed-off-by: Nikolaj Bjorner --- src/api/api_seq.cpp | 2 +- src/api/c++/z3++.h | 7 ++----- src/api/z3_api.h | 11 ++++++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index ca7c860db..e1a6325dc 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -198,7 +198,7 @@ extern "C" { svector buff; for (unsigned i = 0; i < str.length(); ++i) { unsigned ch = str[i]; - if (ch <= 32 || ch >= 127 || (ch == '\\' && i + 1 < str.length() && str[i+1] == 'u')) { + if (ch == 0 || ch >= 256 || (ch == '\\' && i + 1 < str.length() && str[i+1] == 'u')) { buff.reset(); buffer.push_back('\\'); buffer.push_back('u'); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index f81bcb6f8..d25443617 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1120,12 +1120,9 @@ namespace z3 { std::u32string get_u32string() const { assert(is_string_value()); unsigned n = Z3_get_string_length(ctx(), m_ast); - std::vector buffer; - buffer.resize(n); - Z3_get_string_contents(ctx(), m_ast, n, buffer.data()); std::u32string s; - for (auto ch : buffer) - s.push_back(ch); + s.resize(n); + Z3_get_string_contents(ctx(), m_ast, n, (unsigned*)s.data()); return s; } diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 7d252fd26..a1b37cc8b 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3480,6 +3480,11 @@ extern "C" { /** \brief Create a string constant out of the string that is passed in + The string may contain escape encoding for non-printable characters + or characters outside of the basic printable ASCII range. For example, + the escape encoding \u{0} represents the character 0 and the encoding + \u{100} represents the character 256. + def_API('Z3_mk_string', AST, (_in(CONTEXT), _in(STRING))) */ Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s); @@ -3487,7 +3492,8 @@ extern "C" { /** \brief Create a string constant out of the string that is passed in It takes the length of the string as well to take into account - 0 characters. The string is unescaped. + 0 characters. The string is treated as if it is unescaped so a sequence + of characters \u{0} is treated as 5 characters and not the character 0. def_API('Z3_mk_lstring', AST, (_in(CONTEXT), _in(UINT), _in(STRING))) */ @@ -3511,6 +3517,7 @@ extern "C" { /** \brief Retrieve the string constant stored in \c s. + Characters outside the basic printiable ASCII range are escaped. \pre Z3_is_string(c, s) @@ -3520,6 +3527,8 @@ extern "C" { /** \brief Retrieve the string constant stored in \c s. The string can contain escape sequences. + Characters in the range 1 to 255 are literal. + Characters in the range 0, and 256 above are escaped. \pre Z3_is_string(c, s) From 066076557fa3dc7ade8324f559f61e6ff05b1420 Mon Sep 17 00:00:00 2001 From: Weng Shiwei Date: Mon, 25 Oct 2021 05:21:02 -0400 Subject: [PATCH 50/89] Add post-install testing for ocaml binding. (#5617) * Add path flags for cc loader (linux). * Fix os linking and loading problem (maybe on #4840). * Add post-install test of OCaml binding on ubuntu. * Minor. * Tentative CI for macos. --- azure-pipelines.yml | 86 +++++++++++++++++++++++++++++++++++++++++++-- scripts/mk_util.py | 45 ++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 5 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ee0001385..e805c4a2c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -44,7 +44,42 @@ jobs: - template: scripts/test-regressions.yml - job: "Ubuntu20OCaml" - displayName: "Ubuntu 20 with ocaml" + displayName: "Ubuntu 20 with OCaml" + pool: + vmImage: "Ubuntu-20.04" + steps: + - script: sudo apt-get install ocaml opam libgmp-dev + - script: opam init -y + - script: eval `opam config env`; opam install zarith ocamlfind -y + - script: eval `opam config env`; python scripts/mk_make.py --ml + - script: | + set -e + cd build + eval `opam config env` + make -j3 + make -j3 examples + make -j3 test-z3 + ./ml_example.byte + ./ml_example + cd .. + - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.* + - script: | + set -e + cd build + eval `opam config env` + make -j3 + make -j3 _ex_ml_example_post_install + ./ml_example_shared.byte + ./ml_example_shared_custom.byte + ./ml_example_shared + cd .. + - template: scripts/test-z3.yml + - template: scripts/test-regressions.yml + - template: scripts/generate-doc.yml + + +- job: "Ubuntu20OCamlStatic" + displayName: "Ubuntu 20 with OCaml on z3-static" pool: vmImage: "Ubuntu-20.04" steps: @@ -55,17 +90,28 @@ jobs: - script: | set -e cd build - eval `opam config env` + eval `opam config env` make -j3 make -j3 examples make -j3 test-z3 + ./ml_example.byte ./ml_example cd .. + - script: eval `opam config env`; ocamlfind install z3-static build/api/ml/* build/libz3-static.a + - script: | + set -e + cd build + eval `opam config env` + make -j3 + make -j3 _ex_ml_example_post_install + ./ml_example_static.byte + ./ml_example_static_custom.byte + ./ml_example_static + cd .. - template: scripts/test-z3.yml - template: scripts/test-regressions.yml - template: scripts/generate-doc.yml - - job: "LinuxMSan" displayName: "Ubuntu build - cmake" condition: eq(0,1) @@ -252,3 +298,37 @@ jobs: # - template: scripts/test-examples-cmake.yml - template: scripts/test-regressions.yml # - template: scripts/test-java-cmake.yml + + +- job: "MacOSOCaml" + displayName: "MacOS build with OCaml" + pool: + vmImage: "macOS-latest" + steps: + - script: brew install opam + - script: opam init -y + - script: eval `opam config env`; opam install zarith ocamlfind -y + - script: eval `opam config env`; python scripts/mk_make.py --ml + - script: | + set -e + cd build + make -j3 + make -j3 examples + make -j3 test-z3 + ./ml_example.byte + ./ml_example + cd .. + - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.* + - script: | + set -e + cd build + eval `opam config env` + make -j3 + make -j3 _ex_ml_example_post_install + ./ml_example_shared.byte + ./ml_example_shared_custom.byte + ./ml_example_shared + cd .. +# Skip as dead-slow in debug mode: +# - template: scripts/test-z3.yml + - template: scripts/test-regressions.yml \ No newline at end of file diff --git a/scripts/mk_util.py b/scripts/mk_util.py index baa00b191..8f664b942 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2013,9 +2013,12 @@ class MLComponent(Component): LIBZ3 = z3linkdep LIBZ3 = LIBZ3 + ' ' + ' '.join(map(lambda x: '-cclib ' + x, LDFLAGS.split())) + + stubs_install_path = '$$(%s printconf path)/stublibs' % OCAMLFIND if not STATIC_LIB: - dllpath = '-dllpath $$(%s printconf path)/stublibs' % OCAMLFIND - LIBZ3 = LIBZ3 + ' ' + dllpath + loadpath = '-ccopt -L' + stubs_install_path + dllpath = '-dllpath ' + stubs_install_path + LIBZ3 = LIBZ3 + ' ' + loadpath + ' ' + dllpath if DEBUG_MODE and not(is_cygwin()): # Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well. @@ -2036,6 +2039,9 @@ class MLComponent(Component): out.write('\n') out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls)) + if IS_OSX: + out.write('\tinstall_name_tool -id libz3.dylib %s/libz3.dylib libz3.dylib\n' % (stubs_install_path)) + out.write('\tinstall_name_tool -change libz3.dylib %s/libz3.dylib api/ml/dllz3ml.so\n' % (stubs_install_path)) out.write('\n') if IS_WINDOWS: @@ -2294,6 +2300,41 @@ class MLExampleComponent(ExampleComponent): out.write('\n') out.write('_ex_%s: ml_example.byte ml_example$(EXE_EXT)\n\n' % self.name) + debug_opt = '-g ' if DEBUG_MODE else '' + + if STATIC_LIB: + opam_z3_opts = '-thread -package z3-static -linkpkg' + ml_post_install_tests = [ + (OCAMLC, 'ml_example_static.byte'), + (OCAMLC + ' -custom', 'ml_example_static_custom.byte'), + (OCAMLOPT, 'ml_example_static$(EXE_EXT)') + ] + else: + opam_z3_opts = '-thread -package z3 -linkpkg' + ml_post_install_tests = [ + (OCAMLC, 'ml_example_shared.byte'), + (OCAMLC + ' -custom', 'ml_example_shared_custom.byte'), + (OCAMLOPT, 'ml_example_shared$(EXE_EXT)') + ] + + for ocaml_compiler, testname in ml_post_install_tests: + out.write(testname + ':') + for mlfile in get_ml_files(self.ex_dir): + out.write(' %s' % os.path.join(self.to_ex_dir, mlfile)) + out.write('\n') + out.write('\tocamlfind %s -o %s %s %s ' % (ocaml_compiler, debug_opt, testname, opam_z3_opts)) + for mlfile in get_ml_files(self.ex_dir): + out.write(' %s/%s' % (self.to_ex_dir, mlfile)) + out.write('\n') + + if STATIC_LIB: + out.write('_ex_ml_example_post_install: ml_example_static.byte ml_example_static_custom.byte ml_example_static$(EXE_EXT)\n') + else: + out.write('_ex_ml_example_post_install: ml_example_shared.byte ml_example_shared_custom.byte ml_example_shared$(EXE_EXT)\n') + + out.write('\n') + + class PythonExampleComponent(ExampleComponent): def __init__(self, name, path): ExampleComponent.__init__(self, name, path) From ec9498e1663b193ff6d601cfd13d3507c5e4bb83 Mon Sep 17 00:00:00 2001 From: Weng Shiwei Date: Mon, 25 Oct 2021 05:21:40 -0400 Subject: [PATCH 51/89] Fix ocaml link and load (#5618) * Add path flags for cc loader (linux). * Fix os linking and loading problem (maybe on #4840). From cd4481bca37e4f636fb9e252dc95f27187966616 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 11:29:20 +0200 Subject: [PATCH 52/89] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e805c4a2c..2fab225ca 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -72,7 +72,7 @@ jobs: ./ml_example_shared.byte ./ml_example_shared_custom.byte ./ml_example_shared - cd .. + cd .. - template: scripts/test-z3.yml - template: scripts/test-regressions.yml - template: scripts/generate-doc.yml From 09bda6f21c4b49c8a71daf42c916afb78c120f81 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 11:30:18 +0200 Subject: [PATCH 53/89] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2fab225ca..ea2229425 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -107,7 +107,7 @@ jobs: ./ml_example_static.byte ./ml_example_static_custom.byte ./ml_example_static - cd .. + cd .. - template: scripts/test-z3.yml - template: scripts/test-regressions.yml - template: scripts/generate-doc.yml From 4b7c08d08d1fb028e6f40e23bfd20f63de933dc9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 11:30:56 +0200 Subject: [PATCH 54/89] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ea2229425..24a7ae1c6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -328,7 +328,7 @@ jobs: ./ml_example_shared.byte ./ml_example_shared_custom.byte ./ml_example_shared - cd .. + cd .. # Skip as dead-slow in debug mode: # - template: scripts/test-z3.yml - template: scripts/test-regressions.yml \ No newline at end of file From 3036b88f094fc5a468aa3ea04fb8fb5de0830563 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 13:35:23 +0200 Subject: [PATCH 55/89] support threading for TRACE mode --- src/math/simplex/bit_matrix.cpp | 1 + src/solver/assertions/asserted_formulas.cpp | 12 +++---- src/util/small_object_allocator.h | 1 + src/util/trace.cpp | 19 ++++++++++ src/util/trace.h | 40 ++++++++++++++++++--- src/util/util.cpp | 18 ---------- src/util/util.h | 28 +-------------- 7 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/math/simplex/bit_matrix.cpp b/src/math/simplex/bit_matrix.cpp index fa4b481b5..097dd4396 100644 --- a/src/math/simplex/bit_matrix.cpp +++ b/src/math/simplex/bit_matrix.cpp @@ -15,6 +15,7 @@ Notes: #include "math/simplex/bit_matrix.h" #include "util/stopwatch.h" +#include "util/trace.h" #include diff --git a/src/solver/assertions/asserted_formulas.cpp b/src/solver/assertions/asserted_formulas.cpp index 9e9b54140..b3be7e8ab 100644 --- a/src/solver/assertions/asserted_formulas.cpp +++ b/src/solver/assertions/asserted_formulas.cpp @@ -213,13 +213,13 @@ void asserted_formulas::force_push() { } void asserted_formulas::pop_scope(unsigned num_scopes) { - if (m_lazy_scopes > 0) { - unsigned n = std::min(num_scopes, m_lazy_scopes); - m_lazy_scopes -= n; - num_scopes -= n; - if (num_scopes == 0) - return; + if (num_scopes <= m_lazy_scopes) { + m_lazy_scopes -= num_scopes; + return; } + num_scopes -= m_lazy_scopes; + m_lazy_scopes = 0; + TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << " of " << m_scopes.size() << "\n";); m_bv_sharing.pop_scope(num_scopes); m_macro_manager.pop_scope(num_scopes); diff --git a/src/util/small_object_allocator.h b/src/util/small_object_allocator.h index 028c79645..05fe32ef0 100644 --- a/src/util/small_object_allocator.h +++ b/src/util/small_object_allocator.h @@ -21,6 +21,7 @@ Revision History: #include "util/machine.h" #include "util/debug.h" +#include "util/trace.h" class small_object_allocator { static const unsigned CHUNK_SIZE = (8192 - sizeof(void*)*2); diff --git a/src/util/trace.cpp b/src/util/trace.cpp index 5e2930999..d6e6323fc 100644 --- a/src/util/trace.cpp +++ b/src/util/trace.cpp @@ -19,6 +19,25 @@ Revision History: #include "util/trace.h" #include "util/str_hashtable.h" +#ifndef SINGLE_THREAD +#include +#include + +static std::mutex g_verbose_mux; +void verbose_lock() { g_verbose_mux.lock(); } +void verbose_unlock() { g_verbose_mux.unlock(); } + +static std::thread::id g_thread_id = std::this_thread::get_id(); +static bool g_is_threaded = false; + +bool is_threaded() { + if (g_is_threaded) return true; + g_is_threaded = std::this_thread::get_id() != g_thread_id; + return g_is_threaded; +} + +#endif + #ifdef _TRACE std::ofstream tout(".z3-trace"); diff --git a/src/util/trace.h b/src/util/trace.h index fad4707c0..2a63bfaf9 100644 --- a/src/util/trace.h +++ b/src/util/trace.h @@ -21,6 +21,33 @@ Revision History: #include +#ifdef SINGLE_THREAD +# define is_threaded() false +#else +bool is_threaded(); +#endif + +#ifdef SINGLE_THREAD +#define LOCK_CODE(CODE) CODE; +#define THREAD_LOCK(CODE) CODE + +#else +void verbose_lock(); +void verbose_unlock(); + +#define LOCK_CODE(CODE) \ + { \ + verbose_lock(); \ + CODE; \ + verbose_unlock(); \ + } + +#define THREAD_LOCK(CODE) if (is_threaded()) { LOCK_CODE(CODE); } else { CODE; } + +#endif + + + #ifdef _TRACE extern std::ofstream tout; #define TRACE_CODE(CODE) { CODE } ((void) 0 ) @@ -48,10 +75,15 @@ static inline void open_trace() {} static inline void finalize_trace() {} #endif -#define TRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n"; CODE tout << "------------------------------------------------\n"; tout.flush(); }) +#define TRACEH(TAG) tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n" +#define TRACEEND tout << "------------------------------------------------\n" +#define TRACEBODY(TAG, CODE) TRACEH(TAG); CODE; TRACEEND; tout.flush() +#define STRACEBODY(CODE) CODE; tout.flush() -#define STRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { CODE tout.flush(); }) +#define TRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { THREAD_LOCK(TRACEBODY(TAG, CODE)); }) -#define SCTRACE(TAG, COND, CODE) TRACE_CODE(if (is_trace_enabled(TAG) && (COND)) { CODE tout.flush(); }) +#define STRACE(TAG, CODE) TRACE_CODE(if (is_trace_enabled(TAG)) { THREAD_LOCK(STRACEBODY(CODE)); }) -#define CTRACE(TAG, COND, CODE) TRACE_CODE(if (is_trace_enabled(TAG) && (COND)) { tout << "-------- [" << TAG << "] " << __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << " ---------\n"; CODE tout << "------------------------------------------------\n"; tout.flush(); }) +#define SCTRACE(TAG, COND, CODE) TRACE_CODE(if (is_trace_enabled(TAG) && (COND)) { THREAD_LOCK(STRACEBODY(CODE)); }) + +#define CTRACE(TAG, COND, CODE) TRACE_CODE(if (is_trace_enabled(TAG) && (COND)) { THREAD_LOCK(TRACEBODY(TAG, CODE)); }) diff --git a/src/util/util.cpp b/src/util/util.cpp index 714c947a9..a59b26990 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -19,14 +19,6 @@ Revision History: #include "util/util.h" -#ifndef SINGLE_THREAD -#include -#include - -static std::mutex g_verbose_mux; -void verbose_lock() { g_verbose_mux.lock(); } -void verbose_unlock() { g_verbose_mux.unlock(); } -#endif static unsigned g_verbosity_level = 0; @@ -44,16 +36,6 @@ void set_verbose_stream(std::ostream& str) { g_verbose_stream = &str; } -#ifndef SINGLE_THREAD -static std::thread::id g_thread_id = std::this_thread::get_id(); -static bool g_is_threaded = false; - -bool is_threaded() { - if (g_is_threaded) return true; - g_is_threaded = std::this_thread::get_id() != g_thread_id; - return g_is_threaded; -} -#endif std::ostream& verbose_stream() { return *g_verbose_stream; diff --git a/src/util/util.h b/src/util/util.h index 8d3682943..4cac2123a 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -169,37 +169,11 @@ void set_verbosity_level(unsigned lvl); unsigned get_verbosity_level(); std::ostream& verbose_stream(); void set_verbose_stream(std::ostream& str); -#ifdef SINGLE_THREAD -# define is_threaded() false -#else -bool is_threaded(); -#endif -#define IF_VERBOSE(LVL, CODE) { \ - if (get_verbosity_level() >= LVL) { \ - if (is_threaded()) { \ - LOCK_CODE(CODE); \ - } \ - else { \ - CODE; \ - } \ - } } ((void) 0) +#define IF_VERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { THREAD_LOCK(CODE); } } ((void) 0) -#ifdef SINGLE_THREAD -#define LOCK_CODE(CODE) CODE; -#else -void verbose_lock(); -void verbose_unlock(); - -#define LOCK_CODE(CODE) \ - { \ - verbose_lock(); \ - CODE; \ - verbose_unlock(); \ - } -#endif template struct default_eq { From 45681b4c6e73ec4165895b2712954a9d71c25274 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 13:43:15 +0200 Subject: [PATCH 56/89] update API type annotation to make it OCaml friendly --- src/api/z3_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index a1b37cc8b..3c530f4d6 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3554,7 +3554,7 @@ extern "C" { def_API('Z3_get_string_contents', VOID, (_in(CONTEXT), _in(AST), _in(UINT), _out_array(2, UINT))) */ - void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned* buffer); + void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned buffer[]); /** \brief Create an empty sequence of the sequence sort \c seq. From 075769c4c002fc1b9aac23db718a1e6064c3a573 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 16:03:40 +0200 Subject: [PATCH 57/89] try get_string contents again Signed-off-by: Nikolaj Bjorner --- src/api/api_seq.cpp | 2 +- src/api/z3_api.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index e1a6325dc..a7eb237b3 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -237,7 +237,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned* contents) { + void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]) { Z3_TRY; LOG_Z3_get_string_contents(c, s, length, contents); RESET_ERROR_CODE(); diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 3c530f4d6..e6cd0a5b3 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3554,7 +3554,7 @@ extern "C" { def_API('Z3_get_string_contents', VOID, (_in(CONTEXT), _in(AST), _in(UINT), _out_array(2, UINT))) */ - void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned buffer[]); + void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]); /** \brief Create an empty sequence of the sequence sort \c seq. From 4cfc73779adec4dc4d37b16ebdcdea06c54e869c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 25 Oct 2021 16:11:43 +0200 Subject: [PATCH 58/89] update build Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 24a7ae1c6..960aaa1cc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -312,6 +312,7 @@ jobs: - script: | set -e cd build + eval `opam config env` make -j3 make -j3 examples make -j3 test-z3 From efcad5ff358a9370e3ea5c57cb85bc59d21cc1af Mon Sep 17 00:00:00 2001 From: Margus Veanes Date: Tue, 26 Oct 2021 00:11:07 -0700 Subject: [PATCH 59/89] fixed nullability bug in the if-then-else info (#5620) --- src/ast/seq_decl_plugin.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 52edbdc80..ebae138e3 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -1703,7 +1703,13 @@ seq_util::rex::info seq_util::rex::info::orelse(seq_util::rex::info const& i) co // unsigned ite_min_length = std::min(min_length, i.min_length); // lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef); // TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted - return info(false, false, false, false, normalized && i.normalized, monadic && i.monadic, singleton && i.singleton, nullable, std::min(min_length, i.min_length), std::max(star_height, i.star_height)); + return info(false, false, false, false, + normalized && i.normalized, + monadic && i.monadic, + singleton && i.singleton, + ((nullable == l_true && i.nullable == l_true) ? l_true : ((nullable == l_false && i.nullable == l_false) ? l_false : l_undef)), + std::min(min_length, i.min_length), + std::max(star_height, i.star_height)); } else return i; From bdecc256197583e02782fd1f4f7456c16165d3d5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Oct 2021 09:48:54 +0200 Subject: [PATCH 60/89] strengthen contract for log_axiom_instantiation #5621 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_theory.cpp | 9 +++++---- src/smt/theory_fpa.cpp | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/smt/smt_theory.cpp b/src/smt/smt_theory.cpp index 60f8dc3ae..69eecffd6 100644 --- a/src/smt/smt_theory.cpp +++ b/src/smt/smt_theory.cpp @@ -208,10 +208,11 @@ namespace smt { } void theory::log_axiom_instantiation(app * r, unsigned axiom_id, unsigned num_bindings, app * const * bindings, unsigned pattern_id, const vector> & used_enodes) { - ast_manager & m = get_manager(); - app_ref _r(r, m); + ast_manager & m = get_manager(); + SASSERT(r->get_ref_count() > 0); std::ostream& out = m.trace_stream(); symbol const & family_name = m.get_family_name(get_family_id()); + if (pattern_id == UINT_MAX) { out << "[inst-discovered] theory-solving " << static_cast(nullptr) << " " << family_name << "#"; if (axiom_id != UINT_MAX) @@ -235,8 +236,8 @@ namespace smt { enode *orig = std::get<0>(n); enode *substituted = std::get<1>(n); if (orig != nullptr) { - quantifier_manager::log_justification_to_root(out, orig, already_visited, ctx, get_manager()); - quantifier_manager::log_justification_to_root(out, substituted, already_visited, ctx, get_manager()); +// quantifier_manager::log_justification_to_root(out, orig, already_visited, ctx, get_manager()); +// quantifier_manager::log_justification_to_root(out, substituted, already_visited, ctx, get_manager()); } } out << "[new-match] " << static_cast(nullptr) << " " << family_name << "#" << axiom_id << " " << family_name << "#" << pattern_id; diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index d1f60eb97..ee547b22d 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -215,11 +215,12 @@ namespace smt { } void theory_fpa::assert_cnstr(expr * e) { + expr_ref _e(e, m); if (m.is_true(e)) return; TRACE("t_fpa_detail", tout << "asserting " << mk_ismt2_pp(e, m) << "\n";); if (m.has_trace_stream()) log_axiom_instantiation(e); ctx.internalize(e, false); - if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; + if (m.has_trace_stream()) m.trace_stream() << "[end-of-instance]\n"; literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); From aa5b4b8c77a1d273d297336e21a43c8e2dd6bc0d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Oct 2021 09:49:38 +0200 Subject: [PATCH 61/89] strengthen contract for log_axiom_instantiation #5621 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_theory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/smt_theory.cpp b/src/smt/smt_theory.cpp index 69eecffd6..0c876e8bc 100644 --- a/src/smt/smt_theory.cpp +++ b/src/smt/smt_theory.cpp @@ -236,8 +236,8 @@ namespace smt { enode *orig = std::get<0>(n); enode *substituted = std::get<1>(n); if (orig != nullptr) { -// quantifier_manager::log_justification_to_root(out, orig, already_visited, ctx, get_manager()); -// quantifier_manager::log_justification_to_root(out, substituted, already_visited, ctx, get_manager()); + quantifier_manager::log_justification_to_root(out, orig, already_visited, ctx, get_manager()); + quantifier_manager::log_justification_to_root(out, substituted, already_visited, ctx, get_manager()); } } out << "[new-match] " << static_cast(nullptr) << " " << family_name << "#" << axiom_id << " " << family_name << "#" << pattern_id; From 61eb8d1908e406dd1f5d1505e4d4193a5f7c71e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Oct 2021 11:39:13 +0200 Subject: [PATCH 62/89] add ref for regression Signed-off-by: Nikolaj Bjorner --- src/smt/smt_theory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index 7799acf24..c47a7bcf7 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -481,6 +481,7 @@ namespace smt { } void log_axiom_unit(app* r) { + expr_ref _r(r, m); log_axiom_instantiation(r); m.trace_stream() << "[end-of-instance]\n"; } From 125eae06bdea90029fe95fc9c64374ea775779a1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Oct 2021 11:54:29 +0200 Subject: [PATCH 63/89] #4869 load datatype parsing for HORN logic Signed-off-by: Nikolaj Bjorner --- src/solver/smt_logics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/smt_logics.cpp b/src/solver/smt_logics.cpp index ec1acfdb7..65d3977ab 100644 --- a/src/solver/smt_logics.cpp +++ b/src/solver/smt_logics.cpp @@ -160,6 +160,6 @@ bool smt_logics::logic_has_pb(symbol const& s) { } bool smt_logics::logic_has_datatype(symbol const& s) { - return s == "QF_FD" || s == "QF_UFDT" || logic_is_all(s) || s == "QF_DT"; + return s == "QF_FD" || s == "QF_UFDT" || logic_is_all(s) || s == "QF_DT" || logic_has_horn(s); } From eb8c8da8a739154e09856acb3bc66ac3951fadc1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 27 Oct 2021 09:38:36 +0200 Subject: [PATCH 64/89] ex handler Signed-off-by: Nikolaj Bjorner --- examples/c++/example.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index ff2470ba7..15a37be31 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -304,7 +304,7 @@ void error_example() { // The next call fails because x is a Boolean. expr n = x + 1; } - catch (exception ex) { + catch (exception & ex) { std::cout << "failed: " << ex << "\n"; } @@ -312,7 +312,7 @@ void error_example() { try { expr arg = to_expr(c, Z3_get_app_arg(c, x, 0)); } - catch (exception ex) { + catch (exception & ex) { std::cout << "failed: " << ex << "\n"; } } From 723b755ca76e1a05e3b0c55299bf1d689ea2604f Mon Sep 17 00:00:00 2001 From: Weng Shiwei Date: Wed, 27 Oct 2021 05:10:45 -0400 Subject: [PATCH 65/89] Fix the command of `install_name_tool -id`. (#5622) * Fix the command of `install_name_tool -id`. * Fix: don't call `ml_example.byte`. --- azure-pipelines.yml | 6 ------ scripts/mk_util.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 960aaa1cc..0e3e442d8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -59,8 +59,6 @@ jobs: make -j3 make -j3 examples make -j3 test-z3 - ./ml_example.byte - ./ml_example cd .. - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.* - script: | @@ -94,8 +92,6 @@ jobs: make -j3 make -j3 examples make -j3 test-z3 - ./ml_example.byte - ./ml_example cd .. - script: eval `opam config env`; ocamlfind install z3-static build/api/ml/* build/libz3-static.a - script: | @@ -316,8 +312,6 @@ jobs: make -j3 make -j3 examples make -j3 test-z3 - ./ml_example.byte - ./ml_example cd .. - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.* - script: | diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 8f664b942..e6d34dc3e 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2040,7 +2040,7 @@ class MLComponent(Component): out.write('\n') out.write('ml: %s.cma %s.cmxa %s.cmxs\n' % (z3mls, z3mls, z3mls)) if IS_OSX: - out.write('\tinstall_name_tool -id libz3.dylib %s/libz3.dylib libz3.dylib\n' % (stubs_install_path)) + out.write('\tinstall_name_tool -id %s/libz3.dylib libz3.dylib\n' % (stubs_install_path)) out.write('\tinstall_name_tool -change libz3.dylib %s/libz3.dylib api/ml/dllz3ml.so\n' % (stubs_install_path)) out.write('\n') From f5f35f87d08e8518c13bde189cd42ccd090a745e Mon Sep 17 00:00:00 2001 From: Alexander Traud Date: Wed, 27 Oct 2021 23:46:31 +0200 Subject: [PATCH 66/89] fix grouping for latest doxygen (#5626) Since doxygen 1.8.16, opening and closing a group must not be done as C comment but as doxygen command. In other words, not one but two asterisk characters are required so that doxygen finds a group. --- examples/c/test_capi.c | 12 ++-- examples/maxsat/maxsat.c | 4 +- src/api/c++/z3++.h | 8 +-- src/api/z3_algebraic.h | 8 +-- src/api/z3_api.h | 120 ++++++++++++++++++------------------ src/api/z3_ast_containers.h | 12 ++-- src/api/z3_fixedpoint.h | 8 +-- src/api/z3_fpa.h | 12 ++-- src/api/z3_optimization.h | 8 +-- src/api/z3_polynomial.h | 8 +-- src/api/z3_rcf.h | 8 +-- src/api/z3_spacer.h | 8 +-- 12 files changed, 108 insertions(+), 108 deletions(-) diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index 4ebe7c314..a9c472f37 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -22,11 +22,11 @@ Copyright (c) 2015 Microsoft Corporation /** \defgroup capi_ex C API examples */ -/*@{*/ +/**@{*/ /** @name Auxiliary Functions */ -/*@{*/ +/**@{*/ /** \brief exit gracefully in case of error. @@ -694,12 +694,12 @@ void display_version() Z3_get_version(&major, &minor, &build, &revision); printf("Z3 %d.%d.%d.%d\n", major, minor, build, revision); } -/*@}*/ +/**@}*/ /** @name Examples */ -/*@{*/ +/**@{*/ /** \brief "Hello world" example: create a Z3 logical context, and delete it. */ @@ -2947,8 +2947,8 @@ void mk_model_example() { Z3_del_context(ctx); } -/*@}*/ -/*@}*/ +/**@}*/ +/**@}*/ diff --git a/examples/maxsat/maxsat.c b/examples/maxsat/maxsat.c index a0c0cfeec..3e8bf76a5 100644 --- a/examples/maxsat/maxsat.c +++ b/examples/maxsat/maxsat.c @@ -15,7 +15,7 @@ Copyright (c) 2015 Microsoft Corporation /** \defgroup maxsat_ex MaxSAT/MaxSMT examples */ -/*@{*/ +/**@{*/ /** \brief Exit gracefully in case of error. @@ -638,5 +638,5 @@ int main(int argc, char * argv[]) { return 0; } -/*@}*/ +/**@}*/ diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index d25443617..4847eeed5 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -36,12 +36,12 @@ Notes: \defgroup cppapi C++ API */ -/*@{*/ +/**@{*/ /** @name C++ API classes and functions */ -/*@{*/ +/**@{*/ /** \brief Z3 C++ namespace @@ -4059,7 +4059,7 @@ namespace z3 { } -/*@}*/ -/*@}*/ +/**@}*/ +/**@}*/ #undef Z3_THROW diff --git a/src/api/z3_algebraic.h b/src/api/z3_algebraic.h index 952692efa..ee8ee21b8 100644 --- a/src/api/z3_algebraic.h +++ b/src/api/z3_algebraic.h @@ -25,10 +25,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Algebraic Numbers */ - /*@{*/ + /**@{*/ /** \brief Return \c true if \c a can be used as value in the Z3 real algebraic number package. @@ -240,8 +240,8 @@ extern "C" { */ unsigned Z3_API Z3_algebraic_get_i(Z3_context c, Z3_ast a); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_api.h b/src/api/z3_api.h index e6cd0a5b3..7d1366329 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -37,11 +37,11 @@ DEFINE_TYPE(Z3_optimize); DEFINE_TYPE(Z3_rcf_num); /** \defgroup capi C API */ -/*@{*/ +/**@{*/ /** @name Types */ -///@{ +/**@{*/ /** Most of the types in the C API are opaque pointers. @@ -1449,7 +1449,7 @@ typedef enum Z3_GOAL_UNDER_OVER } Z3_goal_prec; -///@} +/**@}*/ #ifdef __cplusplus extern "C" { @@ -1514,7 +1514,7 @@ extern "C" { /**@}*/ /** @name Create configuration */ - /*@{*/ + /**@{*/ /** \brief Create a configuration object for the Z3 context object. @@ -1569,10 +1569,10 @@ extern "C" { */ void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value); - /*@}*/ + /**@}*/ /** @name Context and AST Reference Counting */ - /*@{*/ + /**@{*/ /** \brief Create a context using the given configuration. @@ -1678,10 +1678,10 @@ extern "C" { void Z3_API Z3_interrupt(Z3_context c); - /*@}*/ + /**@}*/ /** @name Parameters */ - /*@{*/ + /**@{*/ /** \brief Create a Z3 (empty) parameter set. @@ -1754,10 +1754,10 @@ extern "C" { */ void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d); - /*@}*/ + /**@}*/ /** @name Parameter Descriptions */ - /*@{*/ + /**@{*/ /** \brief Increment the reference counter of the given parameter description set. @@ -1811,10 +1811,10 @@ extern "C" { */ Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p); - /*@}*/ + /**@}*/ /** @name Symbols */ - /*@{*/ + /**@{*/ /** \brief Create a Z3 symbol using an integer. @@ -1843,10 +1843,10 @@ extern "C" { */ Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s); - /*@}*/ + /**@}*/ /** @name Sorts */ - /*@{*/ + /**@{*/ /** \brief Create a free (uninterpreted) type using the given name (symbol). @@ -2150,10 +2150,10 @@ extern "C" { Z3_func_decl* tester, Z3_func_decl accessors[]); - /*@}*/ + /**@}*/ /** @name Constants and Applications */ - /*@{*/ + /**@{*/ /** \brief Declare a constant or function. @@ -2287,10 +2287,10 @@ extern "C" { */ void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body); - /*@}*/ + /**@}*/ /** @name Propositional Logic and Equality */ - /*@{*/ + /**@{*/ /** \brief Create an AST node representing \c true. @@ -2397,10 +2397,10 @@ extern "C" { def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST))) */ Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]); - /*@}*/ + /**@}*/ /** @name Integers and Reals */ - /*@{*/ + /**@{*/ /** \brief Create an AST node representing \ccode{args[0] + ... + args[num_args-1]}. @@ -2573,10 +2573,10 @@ extern "C" { def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1); - /*@}*/ + /**@}*/ /** @name Bit-vectors */ - /*@{*/ + /**@{*/ /** \brief Bitwise negation. @@ -3097,10 +3097,10 @@ extern "C" { def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST))) */ Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2); - /*@}*/ + /**@}*/ /** @name Arrays */ - /*@{*/ + /**@{*/ /** \brief Array read. The argument \c a is the array and \c i is the index of the array that gets read. @@ -3212,10 +3212,10 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k); - /*@}*/ + /**@}*/ /** @name Sets */ - /*@{*/ + /**@{*/ /** \brief Create Set type. @@ -3308,10 +3308,10 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2); - /*@}*/ + /**@}*/ /** @name Numerals */ - /*@{*/ + /**@{*/ /** \brief Create a numeral of a given sort. @@ -3400,10 +3400,10 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits); - /*@}*/ + /**@}*/ /** @name Sequences and regular expressions */ - /*@{*/ + /**@{*/ /** \brief Create a sequence sort out of the sort for the elements. @@ -3859,11 +3859,11 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_char_is_digit(Z3_context c, Z3_ast ch); - /*@}*/ + /**@}*/ /** @name Special relations */ - /*@{*/ + /**@{*/ /** \brief create a linear ordering relation over signature \c a. The relation is identified by the index \c id. @@ -3904,10 +3904,10 @@ extern "C" { */ Z3_func_decl Z3_API Z3_mk_transitive_closure(Z3_context c, Z3_func_decl f); - /*@}*/ + /**@}*/ /** @name Quantifiers */ - /*@{*/ + /**@{*/ /** \brief Create a pattern for quantifier instantiation. @@ -4212,10 +4212,10 @@ extern "C" { Z3_ast body); - /*@}*/ + /**@}*/ /** @name Accessors */ - /*@{*/ + /**@{*/ /** \brief Return \c Z3_INT_SYMBOL if the symbol was constructed using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol @@ -5150,10 +5150,10 @@ extern "C" { def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),)) */ Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c); - /*@}*/ + /**@}*/ /** @name Modifiers */ - /*@{*/ + /**@{*/ /** \brief Update the arguments of term \c a using the arguments \c args. The number of arguments \c num_args should coincide @@ -5196,10 +5196,10 @@ extern "C" { def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT))) */ Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target); - /*@}*/ + /**@}*/ /** @name Models */ - /*@{*/ + /**@{*/ /** \brief Create a fresh model object. It has reference count 0. @@ -5534,10 +5534,10 @@ extern "C" { def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT))) */ Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i); - /*@}*/ + /**@}*/ /** @name Interaction logging */ - /*@{*/ + /**@{*/ /** \brief Log interaction to a file. @@ -5572,10 +5572,10 @@ extern "C" { def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),)) */ void Z3_API Z3_toggle_warning_messages(bool enabled); - /*@}*/ + /**@}*/ /** @name String conversion */ - /*@{*/ + /**@{*/ /** \brief Select mode for the format used for pretty-printing AST nodes. @@ -5662,10 +5662,10 @@ extern "C" { Z3_ast const assumptions[], Z3_ast formula); - /*@}*/ + /**@}*/ /** @name Parser interface */ - /*@{*/ + /**@{*/ /** \brief Parse the given string using the SMT-LIB2 parser. @@ -5709,10 +5709,10 @@ extern "C" { Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str); - /*@}*/ + /**@}*/ /** @name Error Handling */ - /*@{*/ + /**@{*/ #ifndef SAFE_ERRORS /** \brief Return the error code for the last API call. @@ -5755,10 +5755,10 @@ extern "C" { */ Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err); - /*@}*/ + /**@}*/ /** @name Miscellaneous */ - /*@{*/ + /**@{*/ /** \brief Return Z3 version number information. @@ -5819,10 +5819,10 @@ extern "C" { def_API('Z3_finalize_memory', VOID, ()) */ void Z3_API Z3_finalize_memory(void); - /*@}*/ + /**@}*/ /** @name Goals */ - /*@{*/ + /**@{*/ /** \brief Create a goal (aka problem). A goal is essentially a set of formulas, that can be solved and/or transformed using @@ -5972,10 +5972,10 @@ extern "C" { */ Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g, bool include_names); - /*@}*/ + /**@}*/ /** @name Tactics and Probes */ - /*@{*/ + /**@{*/ /** \brief Return a tactic associated with the given name. The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name. @@ -6324,10 +6324,10 @@ extern "C" { */ Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); - /*@}*/ + /**@}*/ /** @name Solvers*/ - /*@{*/ + /**@{*/ /** \brief Create a new solver. This solver is a "combined solver" (see combined_solver module) that internally uses a non-incremental (solver1) and an @@ -6850,10 +6850,10 @@ extern "C" { */ Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s, bool include_names); - /*@}*/ + /**@}*/ /** @name Statistics */ - /*@{*/ + /**@{*/ /** \brief Convert a statistics into a string. @@ -6935,11 +6935,11 @@ extern "C" { */ uint64_t Z3_API Z3_get_estimated_alloc_size(void); - /*@}*/ + /**@}*/ #ifdef __cplusplus } #endif // __cplusplus -/*@}*/ +/**@}*/ diff --git a/src/api/z3_ast_containers.h b/src/api/z3_ast_containers.h index e317460f8..1db7bf4ce 100644 --- a/src/api/z3_ast_containers.h +++ b/src/api/z3_ast_containers.h @@ -23,10 +23,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name AST vectors */ - /*@{*/ + /**@{*/ /** \brief Return an empty AST vector. @@ -104,10 +104,10 @@ extern "C" { */ Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v); - /*@}*/ + /**@}*/ /** @name AST maps */ - /*@{*/ + /**@{*/ /** \brief Return an empty mapping from AST to AST @@ -189,8 +189,8 @@ extern "C" { def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP))) */ Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_fixedpoint.h b/src/api/z3_fixedpoint.h index b9fada51d..5eadaaf46 100644 --- a/src/api/z3_fixedpoint.h +++ b/src/api/z3_fixedpoint.h @@ -23,10 +23,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Fixedpoint facilities */ - /*@{*/ + /**@{*/ /** \brief Create a new fixedpoint context. @@ -373,8 +373,8 @@ extern "C" { void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index fe85e702e..4ab0d9177 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -23,10 +23,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Floating-Point Arithmetic */ - /*@{*/ + /**@{*/ /** \brief Create the RoundingMode sort. @@ -841,7 +841,7 @@ extern "C" { /** @name Z3-specific floating-point extensions */ - /*@{*/ + /**@{*/ /** \brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort. @@ -1080,9 +1080,9 @@ extern "C" { def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT))) */ Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s); - /*@}*/ - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index 51d8853e0..3cdacc46d 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -28,10 +28,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Optimization facilities */ - /*@{*/ + /**@{*/ /** \brief Create a new optimize context. @@ -368,8 +368,8 @@ extern "C" { Z3_model_eh model_eh); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_polynomial.h b/src/api/z3_polynomial.h index 47979fc87..6e7a72df3 100644 --- a/src/api/z3_polynomial.h +++ b/src/api/z3_polynomial.h @@ -24,11 +24,11 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Polynomials */ - /*@{*/ + /**@{*/ /** \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. @@ -43,8 +43,8 @@ extern "C" { Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_rcf.h b/src/api/z3_rcf.h index f61da20c5..88c27db61 100644 --- a/src/api/z3_rcf.h +++ b/src/api/z3_rcf.h @@ -26,10 +26,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Real Closed Fields */ - /*@{*/ + /**@{*/ /** \brief Delete a RCF numeral created using the RCF API. @@ -196,8 +196,8 @@ extern "C" { */ void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } diff --git a/src/api/z3_spacer.h b/src/api/z3_spacer.h index fe4086e5a..dd1028433 100644 --- a/src/api/z3_spacer.h +++ b/src/api/z3_spacer.h @@ -23,10 +23,10 @@ extern "C" { #endif // __cplusplus /** \defgroup capi C API */ - /*@{*/ + /**@{*/ /** @name Spacer facilities */ - /*@{*/ + /**@{*/ /** \brief Pose a query against the asserted rules at the given level. @@ -132,8 +132,8 @@ extern "C" { Z3_ast_vector vars, Z3_ast body); - /*@}*/ - /*@}*/ + /**@}*/ + /**@}*/ #ifdef __cplusplus } From 4dad41416136ab421be54b4ee56fd17a9c6a20cd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 28 Oct 2021 05:49:07 +0200 Subject: [PATCH 67/89] fix performance regression after adding user declared functions to model Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 3 +-- src/model/model.cpp | 24 +++++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 1e5ce62f3..845ac645c 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1828,9 +1828,8 @@ void cmd_context::add_declared_functions(model& mdl) { mdl.register_decl(f, fi); } } - mdl.add_rec_funs(); } - + mdl.add_rec_funs(); } void cmd_context::display_sat_result(lbool r) { diff --git a/src/model/model.cpp b/src/model/model.cpp index e93e80bfe..21938766b 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -238,15 +238,13 @@ void model::compress(bool force_inline) { top_sort ts(m); collect_deps(ts); ts.topological_sort(); - for (func_decl * f : ts.top_sorted()) { + for (func_decl * f : ts.top_sorted()) cleanup_interp(ts, f, force_inline); - } func_decl_set removed; ts.m_occur_count.reset(); - for (func_decl * f : ts.top_sorted()) { + for (func_decl * f : ts.top_sorted()) collect_occs(ts, f); - } // remove auxiliary declarations that are not used. for (func_decl * f : ts.top_sorted()) { @@ -256,7 +254,8 @@ void model::compress(bool force_inline) { removed.insert(f); } } - if (removed.empty()) break; + if (removed.empty()) + break; TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";); remove_decls(m_decls, removed); remove_decls(m_func_decls, removed); @@ -268,12 +267,14 @@ void model::compress(bool force_inline) { void model::collect_deps(top_sort& ts) { - for (auto const& kv : m_finterp) { - ts.insert(kv.m_key, collect_deps(ts, kv.m_value)); - } - for (auto const& kv : m_interp) { - ts.insert(kv.m_key, collect_deps(ts, kv.m_value.second)); - } + recfun::util u(m); + for (auto const& [f, v] : m_finterp) + if (!u.has_def(f)) + ts.insert(f, collect_deps(ts, v)); + + for (auto const& [f,v] : m_interp) + if (!u.has_def(f)) + ts.insert(f, collect_deps(ts, v.second)); } struct model::deps_collector { @@ -334,6 +335,7 @@ model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) { */ void model::cleanup_interp(top_sort& ts, func_decl* f, bool force_inline) { + unsigned pid = ts.partition_id(f); expr * e1 = get_const_interp(f); if (e1) { From 780761a29e856d2b20a562c40b3c235ee146f322 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 15:27:49 +0200 Subject: [PATCH 68/89] Create wasm.yml initial wasm --- .github/workflows/wasm.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/wasm.yml diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml new file mode 100644 index 000000000..92b4c1c8a --- /dev/null +++ b/.github/workflows/wasm.yml @@ -0,0 +1,30 @@ +name: WASM Build + +on: + push: + branches: [ master ] + +env: + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Configure CMake and build + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_SYSTEM_NAME=WASM -DCMAKE_SYSTEM_VERSION=21 + make -j $(nproc) + tar -cvf z3-build-wasm.tar *.jar *.so + + - name: Archive production artifacts + uses: actions/upload-artifact@v2 + with: + name: build-wasm + path: build/z3-build-wasm.tar From d1592c6abf7fa3c9067e47eb40e6fa32d7dc0817 Mon Sep 17 00:00:00 2001 From: Alexander Traud Date: Fri, 29 Oct 2021 15:34:28 +0200 Subject: [PATCH 69/89] fix misspelled \brief for doxygen (#5632) --- src/model/array_factory.cpp | 2 +- src/muz/spacer/spacer_mbc.h | 2 +- src/smt/seq_offset_eq.h | 2 +- src/smt/smt_relevancy.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/model/array_factory.cpp b/src/model/array_factory.cpp index 6fab80661..d3ec56d39 100644 --- a/src/model/array_factory.cpp +++ b/src/model/array_factory.cpp @@ -38,7 +38,7 @@ array_factory::array_factory(ast_manager & m, model_core & md): } /** - \brieft Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s. + \brief Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s. Store in fi the function interpretation for f. */ expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) { diff --git a/src/muz/spacer/spacer_mbc.h b/src/muz/spacer/spacer_mbc.h index 5c61f2d7b..19bbe839d 100644 --- a/src/muz/spacer/spacer_mbc.h +++ b/src/muz/spacer/spacer_mbc.h @@ -34,7 +34,7 @@ public: typedef obj_map partition_map; /** - \Brief Model Based Cartesian projection of lits + \brief Model Based Cartesian projection of lits */ void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, vector &res); diff --git a/src/smt/seq_offset_eq.h b/src/smt/seq_offset_eq.h index e3c138f9e..3535343fd 100644 --- a/src/smt/seq_offset_eq.h +++ b/src/smt/seq_offset_eq.h @@ -42,7 +42,7 @@ namespace smt { seq_offset_eq(theory& th, ast_manager& m); bool empty() const { return m_offset_equalities.empty(); } /** - \breif determine if r1 = r2 + offset + \brief determine if r1 = r2 + offset */ bool find(enode* r1, enode* r2, int& offset) const; bool contains(enode* r1, enode* r2) const { int offset = 0; return find(r1, r2, offset); } diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index 53bf4807d..87cc3847c 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -169,12 +169,12 @@ namespace smt { bool enabled() const; /** - \Brief Return the region allocator for the smt::context that owns this propagator. + \brief Return the region allocator for the smt::context that owns this propagator. */ region & get_region() const; /** - \Brief Return the ast_manager for the smt::context that owns this propagator. + \brief Return the ast_manager for the smt::context that owns this propagator. */ ast_manager & get_manager() const; From 1d45a331631d6bcf5c774d2ab0e864ee94484a25 Mon Sep 17 00:00:00 2001 From: Alexander Traud Date: Fri, 29 Oct 2021 15:35:05 +0200 Subject: [PATCH 70/89] fix one typo and two misunderstandings for doxygen (#5633) --- src/muz/rel/dl_mk_simple_joins.cpp | 2 +- src/qe/nlarith_util.h | 2 -- src/sat/smt/sat_th.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index c556e37e4..11e366f90 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -42,7 +42,7 @@ namespace datalog { /** \brief Number of rules longer than two that contain this pair. - This number is being updated by \c add_rule and \remove rule. Even though between + This number is being updated by \c add_rule and \c remove_rule. Even though between adding a rule and removing it, the length of a rule can decrease without this pair being notified about it, it will surely see the decrease from length 3 to 2 which the threshold for rule being counted in this counter. diff --git a/src/qe/nlarith_util.h b/src/qe/nlarith_util.h index 4c6f0cd18..acbe4889e 100644 --- a/src/qe/nlarith_util.h +++ b/src/qe/nlarith_util.h @@ -96,8 +96,6 @@ namespace nlarith { bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc); /** \brief Extract non-linear variables from ground formula. - - \requires a ground formula. */ void extract_non_linear(expr* e, ptr_vector& nl_vars); diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index bb9358e59..7e66a7156 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -52,7 +52,7 @@ namespace euf { virtual void apply_sort_cnstr(enode* n, sort* s) {} /** - \record that an equality has been internalized. + \brief Record that an equality has been internalized. */ virtual void eq_internalized(enode* n) {} From 96671cfc73766568988ec876894c8f6f3bf5bebf Mon Sep 17 00:00:00 2001 From: Henrich Lauko Date: Fri, 29 Oct 2021 15:42:32 +0200 Subject: [PATCH 71/89] Add and fix a few general compiler warnings. (#5628) * rewriter: fix unused variable warnings * cmake: make missing non-virtual dtors error * treewide: add missing virtual destructors * cmake: add a few more checks * api: add missing virtual destructor to user_propagator_base * examples: compile cpp example with compiler warnings * model: fix unused variable warnings * rewriter: fix logical-op-parentheses warnings * sat: fix unused variable warnings * smt: fix unused variable warnings --- cmake/compiler_warnings.cmake | 21 +++++++++++++++++++ examples/c++/CMakeLists.txt | 2 ++ src/api/c++/z3++.h | 2 ++ src/ast/ast.h | 1 + src/ast/ast_smt_pp.h | 1 + src/ast/fpa/fpa2bv_converter.h | 2 +- src/ast/is_variable_test.h | 1 + src/ast/normal_forms/name_exprs.h | 1 + src/ast/num_occurs.h | 2 ++ src/ast/recfun_decl_plugin.h | 1 + src/ast/rewriter/push_app_ite.h | 1 + src/ast/rewriter/seq_eq_solver.h | 1 + src/ast/rewriter/seq_rewriter.cpp | 7 +++---- src/math/hilbert/heap_trie.h | 1 + src/math/lp/column_namer.h | 1 + src/math/lp/factorization.h | 2 ++ src/math/lp/lp_settings.h | 1 + src/math/polynomial/polynomial.h | 3 +++ .../upolynomial_factorization_int.h | 2 ++ src/math/realclosure/realclosure.h | 1 + src/math/subpaving/subpaving_types.h | 1 + src/model/model_evaluator.cpp | 1 - src/muz/base/dl_engine_base.h | 1 + src/nlsat/nlsat_solver.h | 1 + src/opt/opt_context.h | 1 + src/opt/opt_pareto.h | 1 + src/qe/nlarith_util.cpp | 1 + src/qe/qe.h | 1 + src/sat/sat_extension.h | 1 + src/sat/sat_solver.cpp | 1 - src/sat/smt/array_model.cpp | 2 +- src/sat/smt/pb_constraint.h | 3 +++ src/sat/smt/pb_solver_interface.h | 1 + src/smt/mam.cpp | 1 - src/smt/smt_model_finder.cpp | 1 + src/smt/theory_opt.h | 1 + src/solver/assertions/asserted_formulas.h | 1 + src/solver/solver.h | 1 + src/tactic/fd_solver/smtfd_solver.cpp | 2 ++ 39 files changed, 68 insertions(+), 9 deletions(-) diff --git a/cmake/compiler_warnings.cmake b/cmake/compiler_warnings.cmake index 4a5819fe0..339c2e6f5 100644 --- a/cmake/compiler_warnings.cmake +++ b/cmake/compiler_warnings.cmake @@ -39,6 +39,27 @@ set(CLANG_WARNINGS_AS_ERRORS "-Werror=delete-non-virtual-dtor" # https://clang.llvm.org/docs/DiagnosticsReference.html#woverloaded-virtual "-Werror=overloaded-virtual" + # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to + # track down memory errors + "-Werror=non-virtual-dtor" + # warn if a null dereference is detected + "-Werror=null-dereference" + # warn for potential performance problem casts + # "-Werror=cast-align" + # warn if float is implicit promoted to double + # "-Werror=double-promotion" + "-Werror=no-unreachable-code-return" + # warn the user if a variable declaration shadows one from a parent context + # "-Werror=shadow" + # warn for c-style casts + # "-Werror=old-style-cast" + # warn on sign conversions + # "-Werror=sign-conversion" + # warn on type conversions that may lose data + # "-Werror=conversion" + # warn on anything being unused + # "-Werror=unused" ) ################################################################################ diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index 6223ea7c7..0b152f6ac 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -28,6 +28,8 @@ add_executable(cpp_example example.cpp) target_include_directories(cpp_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) target_link_libraries(cpp_example PRIVATE ${Z3_LIBRARIES}) +target_compile_options(cpp_example PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) + if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 4847eeed5..1bdcb7c30 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -3940,6 +3940,8 @@ namespace z3 { virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; + virtual ~user_propagator_base() = default; + /** \brief user_propagators created using \c fresh() are created during search and their lifetimes are restricted to search time. They should diff --git a/src/ast/ast.h b/src/ast/ast.h index 0fafc2a76..739f63dc8 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1399,6 +1399,7 @@ inline bool has_labels(expr const * n) { class some_value_proc { public: virtual expr * operator()(sort * s) = 0; + virtual ~some_value_proc() = default; }; // ----------------------------------- diff --git a/src/ast/ast_smt_pp.h b/src/ast/ast_smt_pp.h index 23b656c12..ff25f1235 100644 --- a/src/ast/ast_smt_pp.h +++ b/src/ast/ast_smt_pp.h @@ -46,6 +46,7 @@ public: public: virtual bool operator()(func_decl* d) const { return false; } virtual bool operator()(sort* s) const { return false; } + virtual ~is_declared() = default; }; private: ast_manager& m_manager; diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index 3a1f2ec50..471a7c6fc 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -61,7 +61,7 @@ protected: public: fpa2bv_converter(ast_manager & m); - ~fpa2bv_converter(); + virtual ~fpa2bv_converter(); fpa_util & fu() { return m_util; } bv_util & bu() { return m_bv_util; } diff --git a/src/ast/is_variable_test.h b/src/ast/is_variable_test.h index 461fca59d..6bf200b30 100644 --- a/src/ast/is_variable_test.h +++ b/src/ast/is_variable_test.h @@ -23,6 +23,7 @@ Revision History: class is_variable_proc { public: + virtual ~is_variable_proc() = default; virtual bool operator()(const expr* e) const = 0; }; diff --git a/src/ast/normal_forms/name_exprs.h b/src/ast/normal_forms/name_exprs.h index e744dfb02..268df8821 100644 --- a/src/ast/normal_forms/name_exprs.h +++ b/src/ast/normal_forms/name_exprs.h @@ -23,6 +23,7 @@ Notes: class expr_predicate { public: + virtual ~expr_predicate() = default; virtual bool operator()(expr * t) = 0; }; diff --git a/src/ast/num_occurs.h b/src/ast/num_occurs.h index cff4ef9ff..d8c6e6319 100644 --- a/src/ast/num_occurs.h +++ b/src/ast/num_occurs.h @@ -37,6 +37,8 @@ public: m_ignore_quantifiers(ignore_quantifiers) { } + virtual ~num_occurs() = default; + void validate(); virtual void reset() { m_num_occurs.reset(); } diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index dd6f5181a..1146b7aaf 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -93,6 +93,7 @@ namespace recfun { // closure for computing whether a `rhs` expression is immediate struct is_immediate_pred { virtual bool operator()(expr * rhs) = 0; + virtual ~is_immediate_pred() = default; }; class def { diff --git a/src/ast/rewriter/push_app_ite.h b/src/ast/rewriter/push_app_ite.h index ae3af1222..a2e18dd25 100644 --- a/src/ast/rewriter/push_app_ite.h +++ b/src/ast/rewriter/push_app_ite.h @@ -33,6 +33,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg { virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args); br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr); push_app_ite_cfg(ast_manager& m): m(m), m_conservative(true) {} + virtual ~push_app_ite_cfg() = default; void set_conservative(bool c) { m_conservative = c; } bool rewrite_patterns() const { return false; } }; diff --git a/src/ast/rewriter/seq_eq_solver.h b/src/ast/rewriter/seq_eq_solver.h index 6be2531b2..c6c5437b7 100644 --- a/src/ast/rewriter/seq_eq_solver.h +++ b/src/ast/rewriter/seq_eq_solver.h @@ -39,6 +39,7 @@ namespace seq { class eq_solver_context { public: + virtual ~eq_solver_context() = default; virtual void add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0; virtual void add_solution(expr* var, expr* term) = 0; virtual expr* expr2rep(expr* e) = 0; diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index f75ccb255..588979534 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -3046,7 +3046,6 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref auto nothing = [&]() { return expr_ref(re().mk_empty(r->get_sort()), m()); }; auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); }; auto dotstar = [&]() { return expr_ref(re().mk_full_seq(r->get_sort()), m()); }; - auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(r->get_sort())), m()); }; unsigned lo = 0, hi = 0; if (re().is_empty(r) || re().is_epsilon(r)) // D(e,[]) = D(e,()) = [] @@ -3182,7 +3181,7 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref else if (re().is_loop(r, r1, lo)) result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, lo - 1)); else if (re().is_loop(r, r1, lo, hi)) { - if (lo == 0 && hi == 0 || hi < lo) + if ((lo == 0 && hi == 0) || hi < lo) result = nothing(); else result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, (lo == 0 ? 0 : lo - 1), hi - 1)); @@ -3260,7 +3259,7 @@ expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) { } expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* d) { - sort* seq_sort = nullptr, * ele_sort = nullptr; + sort* seq_sort = nullptr; VERIFY(m_util.is_re(d, seq_sort)); auto nothing = [&]() { return expr_ref(re().mk_empty(d->get_sort()), m()); }; auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); }; @@ -3404,7 +3403,7 @@ expr_ref seq_rewriter::simplify_path(expr* path) { result = simplify_path(t); else if (m().is_true(t)) result = simplify_path(h); - else if (m().is_eq(h, lhs, rhs) || m().is_not(h, h1) && m().is_eq(h1, lhs, rhs)) + else if (m().is_eq(h, lhs, rhs) || (m().is_not(h, h1) && m().is_eq(h1, lhs, rhs))) elim_condition(lhs, result); } return result; diff --git a/src/math/hilbert/heap_trie.h b/src/math/hilbert/heap_trie.h index 8b9f60f73..7d4179de8 100644 --- a/src/math/hilbert/heap_trie.h +++ b/src/math/hilbert/heap_trie.h @@ -270,6 +270,7 @@ public: class check_value { public: virtual bool operator()(Value const& v) = 0; + virtual ~check_value() = default; }; bool find_le(Key const* keys, check_value& check) { diff --git a/src/math/lp/column_namer.h b/src/math/lp/column_namer.h index cffae412c..cef58ed21 100644 --- a/src/math/lp/column_namer.h +++ b/src/math/lp/column_namer.h @@ -23,6 +23,7 @@ Revision History: namespace lp { class column_namer { public: + virtual ~column_namer() = default; virtual std::string get_variable_name(unsigned j) const = 0; template std::ostream & print_row(const row_strip & row, std::ostream & out) const { diff --git a/src/math/lp/factorization.h b/src/math/lp/factorization.h index a021a2a36..b233894ad 100644 --- a/src/math/lp/factorization.h +++ b/src/math/lp/factorization.h @@ -120,6 +120,8 @@ struct factorization_factory { m_vars(vars), m_monic(m) { } + virtual ~factorization_factory() = default; + bool_vector get_mask() const { // we keep the last element always in the first factor to avoid // repeating a pair twice, that is why m_mask is shorter by one then m_vars diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 594e9b6f4..442d10b16 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -99,6 +99,7 @@ template bool is_epsilon_small(const X & v, const double& eps); class lp_resource_limit { public: + virtual ~lp_resource_limit() = default; virtual bool get_cancel_flag() = 0; }; diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index 20c9b6b40..416422f64 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -71,6 +71,7 @@ namespace polynomial { template class var2value { public: + virtual ~var2value() = default; virtual ValManager & m() const = 0; virtual bool contains(var x) const = 0; virtual Value const & operator()(var x) const = 0; @@ -100,6 +101,7 @@ namespace polynomial { struct display_var_proc { virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; } + virtual ~display_var_proc() = default; }; class polynomial; @@ -228,6 +230,7 @@ namespace polynomial { del_eh * m_next; public: del_eh():m_next(nullptr) {} + virtual ~del_eh() = default; virtual void operator()(polynomial * p) = 0; }; diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index e3e4793a5..e66fd2f1b 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -175,6 +175,8 @@ namespace upolynomial { m_current_size = 0; } + virtual ~factorization_combination_iterator_base() = default; + /** \brief Returns the factors we are enumerating through. */ diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index 8154c43d3..788db4bbf 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -37,6 +37,7 @@ namespace realclosure { class mk_interval { public: + virtual ~mk_interval() = default; virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0; }; diff --git a/src/math/subpaving/subpaving_types.h b/src/math/subpaving/subpaving_types.h index b914901a8..874013533 100644 --- a/src/math/subpaving/subpaving_types.h +++ b/src/math/subpaving/subpaving_types.h @@ -42,6 +42,7 @@ public: }; struct display_var_proc { + virtual ~display_var_proc() = default; virtual void operator()(std::ostream & out, var x) const { out << "x" << x; } }; diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 617cfe85f..5c6d0f311 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -162,7 +162,6 @@ struct evaluator_cfg : public default_rewriter_cfg { result_pr = nullptr; family_id fid = f->get_family_id(); bool _is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f); - func_decl* g = nullptr; br_status st = BR_FAILED; #if 0 struct pp { diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index 05872c06b..fcf45abf9 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -122,6 +122,7 @@ namespace datalog { class register_engine_base { public: + virtual ~register_engine_base() = default; virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0; virtual void set_context(context* ctx) = 0; }; diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index 1e3e5cdfd..c65a2b4ff 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -32,6 +32,7 @@ namespace nlsat { class display_assumption_proc { public: + virtual ~display_assumption_proc() = default; virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0; }; diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 43f776a68..dd717c392 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -45,6 +45,7 @@ namespace opt { class maxsat_context { public: + virtual ~maxsat_context() = default; virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) diff --git a/src/opt/opt_pareto.h b/src/opt/opt_pareto.h index f9c6fd912..a814ac0f8 100644 --- a/src/opt/opt_pareto.h +++ b/src/opt/opt_pareto.h @@ -26,6 +26,7 @@ namespace opt { class pareto_callback { public: + virtual ~pareto_callback() = default; virtual unsigned num_objectives() = 0; virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0; virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0; diff --git a/src/qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp index e5c4300a5..4a4997de6 100644 --- a/src/qe/nlarith_util.cpp +++ b/src/qe/nlarith_util.cpp @@ -989,6 +989,7 @@ namespace nlarith { imp& m_imp; public: isubst(imp& i) : m_imp(i) {} + virtual ~isubst() = default; virtual void mk_lt(poly const& p, app_ref& r) = 0; virtual void mk_eq(poly const& p, app_ref& r) = 0; virtual void mk_le(poly const& p, app_ref& r) { diff --git a/src/qe/qe.h b/src/qe/qe.h index 6a50c0a71..a5152522f 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -34,6 +34,7 @@ namespace qe { class i_nnf_atom { public: + virtual ~i_nnf_atom() = default; virtual void operator()(expr* e, bool pol, expr_ref& result) = 0; }; diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index b185ebdc5..147bc90cc 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -40,6 +40,7 @@ namespace sat { class literal_occs_fun { public: virtual double operator()(literal l) = 0; + virtual ~literal_occs_fun() = default; }; diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index cdf3a4c68..e5fd2ef70 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1893,7 +1893,6 @@ namespace sat { void solver::init_ext_assumptions() { if (m_ext && m_ext->tracking_assumptions()) { m_ext_assumption_set.reset(); - unsigned trail_size = m_trail.size(); if (!inconsistent()) m_ext->add_assumptions(m_ext_assumption_set); } diff --git a/src/sat/smt/array_model.cpp b/src/sat/smt/array_model.cpp index 33ad8e484..92a368095 100644 --- a/src/sat/smt/array_model.cpp +++ b/src/sat/smt/array_model.cpp @@ -119,7 +119,7 @@ namespace array { bool solver::must_have_different_model_values(theory_var v1, theory_var v2) { euf::enode* else1 = nullptr, * else2 = nullptr; - euf::enode* n1 = var2enode(v1), *n2 = var2enode(v2); + euf::enode* n1 = var2enode(v1); expr* e1 = n1->get_expr(); if (!a.is_array(e1)) return true; diff --git a/src/sat/smt/pb_constraint.h b/src/sat/smt/pb_constraint.h index 076151395..d8cec6de9 100644 --- a/src/sat/smt/pb_constraint.h +++ b/src/sat/smt/pb_constraint.h @@ -52,6 +52,9 @@ namespace pb { constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): m_tag(t), m_lit(l), m_size(sz), m_obj_size(osz), m_id(id), m_k(k) { } + + virtual ~constraint() = default; + sat::ext_constraint_idx cindex() const { return sat::constraint_base::mem2base(this); } void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), sat::constraint_base::mem2base_ptr(this)); } unsigned id() const { return m_id; } diff --git a/src/sat/smt/pb_solver_interface.h b/src/sat/smt/pb_solver_interface.h index 30914fc3f..87d6c1b75 100644 --- a/src/sat/smt/pb_solver_interface.h +++ b/src/sat/smt/pb_solver_interface.h @@ -35,6 +35,7 @@ namespace pb { class solver_interface { public: + virtual ~solver_interface() = default; virtual lbool value(bool_var v) const = 0; virtual lbool value(literal lit) const = 0; virtual bool is_false(literal lit) const = 0; diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index bc97daed2..a90403626 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -2091,7 +2091,6 @@ namespace { enode * n = m_registers[j2->m_reg]->get_root(); if (n->get_num_parents() == 0) return nullptr; - unsigned num_args = n->get_num_args(); enode_vector * v = mk_enode_vector(); enode_vector::const_iterator it1 = n->begin_parents(); enode_vector::const_iterator end1 = n->end_parents(); diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 0b973f4ed..34f323695 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -50,6 +50,7 @@ namespace smt { class evaluator { public: + virtual ~evaluator() = default; virtual expr* eval(expr* n, bool model_completion) = 0; }; diff --git a/src/smt/theory_opt.h b/src/smt/theory_opt.h index a9eb2321b..9e2fa9295 100644 --- a/src/smt/theory_opt.h +++ b/src/smt/theory_opt.h @@ -29,6 +29,7 @@ namespace smt { class theory_opt { public: typedef inf_eps_rational inf_eps; + virtual ~theory_opt() = default; virtual inf_eps value(theory_var) = 0; virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) = 0; virtual theory_var add_objective(app* term) = 0; diff --git a/src/solver/assertions/asserted_formulas.h b/src/solver/assertions/asserted_formulas.h index 59f83a34f..95848133c 100644 --- a/src/solver/assertions/asserted_formulas.h +++ b/src/solver/assertions/asserted_formulas.h @@ -71,6 +71,7 @@ class asserted_formulas { char const* m_id; public: simplify_fmls(asserted_formulas& af, char const* id): af(af), m(af.m), m_id(id) {} + virtual ~simplify_fmls() = default; char const* id() const { return m_id; } virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) = 0; virtual bool should_apply() const { return true;} diff --git a/src/solver/solver.h b/src/solver/solver.h index a6d6f8169..550105167 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -241,6 +241,7 @@ public: class propagate_callback { public: + virtual ~propagate_callback() = default; virtual void propagate_cb(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr* conseq) = 0; }; class context_obj { diff --git a/src/tactic/fd_solver/smtfd_solver.cpp b/src/tactic/fd_solver/smtfd_solver.cpp index 3e23175ff..c5d67506e 100644 --- a/src/tactic/fd_solver/smtfd_solver.cpp +++ b/src/tactic/fd_solver/smtfd_solver.cpp @@ -509,6 +509,8 @@ namespace smtfd { m_context.add_plugin(this); } + virtual ~theory_plugin() = default; + table& ast2table(ast* f, sort* s) { unsigned idx = 0; if (!m_ast2table.find(f, s, idx)) { From d1fbf013eb56a894bf5812c265d59cded97cc03d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 15:54:43 +0200 Subject: [PATCH 72/89] Update azure-pipelines.yml make it green --- azure-pipelines.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0e3e442d8..26a29038a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -61,16 +61,6 @@ jobs: make -j3 test-z3 cd .. - script: eval `opam config env`; ocamlfind install z3 build/api/ml/* -dll build/libz3.* - - script: | - set -e - cd build - eval `opam config env` - make -j3 - make -j3 _ex_ml_example_post_install - ./ml_example_shared.byte - ./ml_example_shared_custom.byte - ./ml_example_shared - cd .. - template: scripts/test-z3.yml - template: scripts/test-regressions.yml - template: scripts/generate-doc.yml @@ -326,4 +316,4 @@ jobs: cd .. # Skip as dead-slow in debug mode: # - template: scripts/test-z3.yml - - template: scripts/test-regressions.yml \ No newline at end of file + - template: scripts/test-regressions.yml From e7a54db8b0eb29235bde703c6e361d67addecc24 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Fri, 29 Oct 2021 16:01:06 +0200 Subject: [PATCH 73/89] Use emscripten to create a wasm build (#5634) --- .github/workflows/wasm.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 92b4c1c8a..e5e184028 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -16,15 +16,19 @@ jobs: uses: actions/checkout@v2 - name: Configure CMake and build + uses: mymindstorm/setup-emsdk@v9 run: | mkdir build cd build - cmake -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_SYSTEM_NAME=WASM -DCMAKE_SYSTEM_VERSION=21 - make -j $(nproc) - tar -cvf z3-build-wasm.tar *.jar *.so - - - name: Archive production artifacts - uses: actions/upload-artifact@v2 - with: - name: build-wasm - path: build/z3-build-wasm.tar + + emcmake cmake \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DCMAKE_INSTALL_PREFIX=/emsdk/upstream/emscripten/system \ + -DZ3_BUILD_LIBZ3_SHARED=OFF \ + -DZ3_ENABLE_EXAMPLE_TARGETS=OFF \ + -DZ3_BUILD_TEST_EXECUTABLES=OFF \ + -DZ3_BUILD_EXECUTABLE=OFF \ + -DZ3_SINGLE_THREADED=ON \ + -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \ + ..; \ + make ; make install; \ From fe0e1cce302ddc92d49db194d0c266e015eb08e4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 16:03:22 +0200 Subject: [PATCH 74/89] Update wasm.yml --- .github/workflows/wasm.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index e5e184028..891c08f7e 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -16,8 +16,8 @@ jobs: uses: actions/checkout@v2 - name: Configure CMake and build - uses: mymindstorm/setup-emsdk@v9 - run: | + - uses: mymindstorm/setup-emsdk@v9 + - run: | mkdir build cd build From f83226df9c4fea049b43c09f9ffe8b1308e36774 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 16:07:04 +0200 Subject: [PATCH 75/89] Update wasm.yml --- .github/workflows/wasm.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 891c08f7e..7892b35b7 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -15,9 +15,11 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Import enscripten + uses: mymindstorm/setup-emsdk@v9 + - name: Configure CMake and build - - uses: mymindstorm/setup-emsdk@v9 - - run: | + run: | mkdir build cd build From f61e6abb352e233851ec78155bc8f51b702202f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 16:10:40 +0200 Subject: [PATCH 76/89] Update wasm.yml --- .github/workflows/wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 7892b35b7..2f8aed2b8 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -15,7 +15,7 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Import enscripten + - name: Import emscripten uses: mymindstorm/setup-emsdk@v9 - name: Configure CMake and build From dfba177813766c3219a1d4da8342bba6b9214a27 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 17:02:15 +0200 Subject: [PATCH 77/89] Update wasm.yml --- .github/workflows/wasm.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 2f8aed2b8..b74b9bb77 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -25,12 +25,18 @@ jobs: emcmake cmake \ -DCMAKE_BUILD_TYPE=MinSizeRel \ - -DCMAKE_INSTALL_PREFIX=/emsdk/upstream/emscripten/system \ -DZ3_BUILD_LIBZ3_SHARED=OFF \ -DZ3_ENABLE_EXAMPLE_TARGETS=OFF \ -DZ3_BUILD_TEST_EXECUTABLES=OFF \ -DZ3_BUILD_EXECUTABLE=OFF \ -DZ3_SINGLE_THREADED=ON \ -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \ - ..; \ - make ; make install; \ + ..; + make + tar -cvf z3-build-wasm.tar *.jar *.so + + - name: Archive production artifacts + uses: actions/upload-artifact@v2 + with: + name: z3-build-wasm + path: build/z3-build-${{ matrix.android-abi }}.tar From 933bb4f1f029c1a59574d1dce38b55d03525e228 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 17:03:04 +0200 Subject: [PATCH 78/89] Update wasm.yml --- .github/workflows/wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index b74b9bb77..e4fa283ed 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -39,4 +39,4 @@ jobs: uses: actions/upload-artifact@v2 with: name: z3-build-wasm - path: build/z3-build-${{ matrix.android-abi }}.tar + path: build/z3-build-wasm.tar From 8e59b343382be25a2f5cb404cadedd3a3857e995 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 17:04:42 +0200 Subject: [PATCH 79/89] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 70dc0f8a2..5c5af803b 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z ## Build status -| Azure Pipelines | Code Coverage | Open Bugs | Android Build | -| --------------- | --------------|-----------|---------------| -| [![Build Status](https://dev.azure.com/Z3Public/Z3/_apis/build/status/Z3Prover.z3?branchName=master)](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [![CodeCoverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) |[![Android Build](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | +| Azure Pipelines | Code Coverage | Open Bugs | Android Build | WASM Build | +| --------------- | --------------|-----------|---------------|------------| +| [![Build Status](https://dev.azure.com/Z3Public/Z3/_apis/build/status/Z3Prover.z3?branchName=master)](https://dev.azure.com/Z3Public/Z3/_build/latest?definitionId=1&branchName=master) | [![CodeCoverage](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/coverage.yml) | [![Open Issues](https://github.com/Z3Prover/z3/actions/workflows/wip.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wip.yml) |[![Android Build](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/android-build.yml) | [![WASM Build](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml/badge.svg)](https://github.com/Z3Prover/z3/actions/workflows/wasm.yml) | [1]: #building-z3-on-windows-using-visual-studio-command-prompt [2]: #building-z3-using-make-and-gccclang From a11ca1a1b7bbed703b78e186b484ab531b2ab026 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 17:51:20 +0200 Subject: [PATCH 80/89] Update wasm.yml --- .github/workflows/wasm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index e4fa283ed..0a2ebc3d2 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -33,7 +33,7 @@ jobs: -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \ ..; make - tar -cvf z3-build-wasm.tar *.jar *.so + tar -cvf z3-build-wasm.tar *.a - name: Archive production artifacts uses: actions/upload-artifact@v2 From 036b38a97fedc50f63c5b5735f76cd746b184c8f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Oct 2021 15:31:33 +0200 Subject: [PATCH 81/89] ubuntu 16 is no more Signed-off-by: Nikolaj Bjorner --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 26a29038a..ea894a93a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -132,7 +132,7 @@ jobs: # - template: scripts/test-java-cmake.yml # - template: scripts/test-regressions.yml -- job: "Ubuntu16CMake" +- job: "UbuntuCMake" displayName: "Ubuntu build - cmake" pool: vmImage: "Ubuntu-latest" From a94e2e62af13cb23c14e97f7d4441327550040db Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Oct 2021 11:28:09 +0200 Subject: [PATCH 82/89] build warnings --- src/sat/smt/arith_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 89b778672..76f1d9a0a 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -627,7 +627,7 @@ namespace arith { anum const& an = nl_value(v, *m_a1); if (a.is_int(o) && !m_nla->am().is_int(an)) value = a.mk_numeral(rational::zero(), a.is_int(o)); - value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o)); + //value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o)); } else if (v != euf::null_theory_var) { rational r = get_value(v); From 87d4ce265901d5cfc33f9a617ec2104773c4c370 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 Nov 2021 14:55:28 -0700 Subject: [PATCH 83/89] working on #5614 there are some different sources for the performance regression illustrated by the example. The mitigations will be enabled separately: - m_bv_to_propagate is too expensive - lp_bound_propagator misses equalities in two different ways: - it resets row checks after backtracking even though they could still propagate - it misses equalities for fixed rows when the fixed constant value does not correspond to a fixed variable. FYI @levnach --- src/math/lp/lar_solver.cpp | 51 ++-- src/math/lp/lar_solver.h | 23 +- src/math/lp/lar_term.h | 2 +- src/math/lp/lp_bound_propagator.h | 440 ++++++++++++++++-------------- src/math/lp/lp_settings.cpp | 2 +- src/math/lp/lp_settings.h | 8 +- src/sat/smt/arith_solver.cpp | 13 +- src/sat/smt/arith_solver.h | 2 +- src/smt/smt_context_pp.cpp | 30 +- src/smt/smt_theory.h | 3 - src/smt/theory_arith_aux.h | 14 +- src/smt/theory_arith_pp.h | 100 +++---- src/smt/theory_lra.cpp | 119 ++++---- 13 files changed, 422 insertions(+), 385 deletions(-) diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index b31713094..561a94e99 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -16,6 +16,9 @@ namespace lp { lp_settings const& lar_solver::settings() const { return m_settings; } + statistics& lar_solver::stats() { return m_settings.stats(); } + + void lar_solver::updt_params(params_ref const& _p) { smt_params_helper p(_p); set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows()); @@ -23,17 +26,9 @@ namespace lp { m_settings.updt_params(_p); } - - void clear() { - lp_assert(false); // not implemented - } - lar_solver::lar_solver() : - m_status(lp_status::UNKNOWN), m_crossed_bounds_column(-1), m_mpq_lar_core_solver(m_settings, *this), - m_int_solver(nullptr), - m_need_register_terms(false), m_var_register(false), m_term_register(true), m_constraints(*this) {} @@ -197,11 +192,11 @@ namespace lp { void lar_solver::set_status(lp_status s) { m_status = s; } lp_status lar_solver::find_feasible_solution() { - m_settings.stats().m_make_feasible++; - if (A_r().column_count() > m_settings.stats().m_max_cols) - m_settings.stats().m_max_cols = A_r().column_count(); - if (A_r().row_count() > m_settings.stats().m_max_rows) - m_settings.stats().m_max_rows = A_r().row_count(); + stats().m_make_feasible++; + if (A_r().column_count() > stats().m_max_cols) + stats().m_max_cols = A_r().column_count(); + if (A_r().row_count() > stats().m_max_rows) + stats().m_max_rows = A_r().row_count(); if (strategy_is_undecided()) decide_on_strategy_and_adjust_initial_state(); @@ -248,7 +243,7 @@ namespace lp { m_constraints.push(); m_usage_in_terms.push(); } - + void lar_solver::clean_popped_elements(unsigned n, u_set& set) { vector to_remove; for (unsigned j : set) @@ -269,9 +264,8 @@ namespace lp { m_crossed_bounds_column.pop(k); unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); - if (m_settings.use_tableau()) { + if (m_settings.use_tableau()) pop_tableau(); - } lp_assert(A_r().column_count() == n); TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) { @@ -285,6 +279,10 @@ namespace lp { clean_popped_elements(n, m_columns_with_changed_bounds); clean_popped_elements(n, m_incorrect_columns); + for (auto rid : m_row_bounds_to_replay) + insert_row_with_changed_bounds(rid); + m_row_bounds_to_replay.reset(); + unsigned m = A_r().row_count(); clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); @@ -633,6 +631,9 @@ namespace lp { left_side.push_back(std::make_pair(p.second, p.first)); } + void lar_solver::insert_row_with_changed_bounds(unsigned rid) { + m_rows_with_changed_bounds.insert(rid); + } void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { if (A_r().row_count() != m_column_buffer.data_size()) @@ -643,14 +644,14 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); for (unsigned i : m_column_buffer.m_index) - m_rows_with_changed_bounds.insert(i); + insert_row_with_changed_bounds(i); } void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { for (auto& rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) - m_rows_with_changed_bounds.insert(rc.var()); + insert_row_with_changed_bounds(rc.var()); } bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } @@ -743,7 +744,7 @@ namespace lp { void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { - m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); + insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); return; } @@ -793,7 +794,7 @@ namespace lp { update_x_and_inf_costs_for_columns_with_changed_bounds(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); - lp_assert(((m_settings.stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold()); + lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold()); } @@ -974,9 +975,7 @@ namespace lp { bool lar_solver::the_left_sides_sum_to_zero(const vector>& evidence) const { std::unordered_map coeff_map; - for (auto& it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; + for (auto const & [coeff, con_ind] : evidence) { lp_assert(m_constraints.valid_index(con_ind)); register_in_map(coeff_map, m_constraints[con_ind], coeff); } @@ -1337,7 +1336,7 @@ namespace lp { void lar_solver::mark_rows_for_bound_prop(lpvar j) { auto& column = A_r().m_columns[j]; for (auto const& r : column) - m_rows_with_changed_bounds.insert(r.var()); + insert_row_with_changed_bounds(r.var()); } @@ -1659,7 +1658,7 @@ namespace lp { m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); m_mpq_lar_core_solver.m_r_basis.push_back(j); if (m_settings.bound_propagation()) - m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + insert_row_with_changed_bounds(A_r().row_count() - 1); } else { m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); @@ -1755,7 +1754,7 @@ namespace lp { if (use_tableau() && !coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) - m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + insert_row_with_changed_bounds(A_r().row_count() - 1); } lp_assert(m_var_register.size() == A_r().column_count()); if (m_need_register_terms) { diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index 6e61354ff..b4c69d783 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -76,13 +76,13 @@ class lar_solver : public column_namer { //////////////////// fields ////////////////////////// lp_settings m_settings; - lp_status m_status; + lp_status m_status = lp_status::UNKNOWN; stacked_value m_simplex_strategy; // such can be found at the initialization step: u < l stacked_value m_crossed_bounds_column; lar_core_solver m_mpq_lar_core_solver; - int_solver * m_int_solver; - bool m_need_register_terms; + int_solver * m_int_solver = nullptr; + bool m_need_register_terms = false; var_register m_var_register; var_register m_term_register; stacked_vector m_columns_to_ul_pairs; @@ -90,6 +90,8 @@ class lar_solver : public column_namer { // the set of column indices j such that bounds have changed for j u_set m_columns_with_changed_bounds; u_set m_rows_with_changed_bounds; + unsigned_vector m_row_bounds_to_replay; + u_set m_basic_columns_with_changed_cost; // these are basic columns with the value changed, so the the corresponding row in the tableau // does not sum to zero anymore @@ -164,7 +166,6 @@ class lar_solver : public column_namer { void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); - void clear(); bool use_lu() const; bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; @@ -219,6 +220,7 @@ class lar_solver : public column_namer { void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j); unsigned num_changed_bounds() const { return m_rows_with_changed_bounds.size(); } + void insert_row_with_changed_bounds(unsigned rid); void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); void set_value_for_nbasic_column(unsigned j, const impq & new_val); @@ -368,20 +370,19 @@ public: // these two loops should be run sequentially // since the first loop might change column bounds // and add fixed columns this way - if (settings().cheap_eqs()) { + if (settings().propagate_eqs()) { bp.clear_for_eq(); for (unsigned i : m_rows_with_changed_bounds) { - calculate_cheap_eqs_for_row(i, bp); + unsigned offset_eqs = stats().m_offset_eqs; + bp.cheap_eq_tree(i); if (settings().get_cancel_flag()) return; + if (stats().m_offset_eqs > offset_eqs) + m_row_bounds_to_replay.push_back(i); } } m_rows_with_changed_bounds.clear(); } - template - void calculate_cheap_eqs_for_row(unsigned i, lp_bound_propagator & bp) { - bp.cheap_eq_tree(i); - } bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } @@ -515,6 +516,8 @@ public: unsigned column_to_reported_index(unsigned j) const; lp_settings & settings(); lp_settings const & settings() const; + statistics& stats(); + void updt_params(params_ref const& p); column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; } const impq & get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; } diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 8dcb16684..3ef424e24 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -155,7 +155,7 @@ public: }; class const_iterator { - u_map< mpq>::iterator m_it; + u_map::iterator m_it; public: ival operator*() const { return ival(m_it->m_key, m_it->m_value); } const_iterator operator++() { const_iterator i = *this; m_it++; return i; } diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index fec650293..1ea872517 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -72,14 +72,15 @@ class lp_bound_propagator { static int other(int x, int y, int z) { SASSERT(x == z || y == z); return x == z ? y : x; } std::ostream& print_vert(std::ostream & out, const vertex* v) const { out << "(c = " << v->column() << ", parent = {"; - if (v->parent()) { out << "(" << v->parent()->column() << ")";} - else { out << "null"; } + if (v->parent()) + out << "(" << v->parent()->column() << ")"; + else + out << "null"; out << "} , lvl = " << v->level(); - if (m_pol.contains(v->column())) { + if (m_pol.contains(v->column())) out << (pol(v) == -1? " -":" +"); - } else { + else out << " not in m_pol"; - } out << ')'; return out; } @@ -87,13 +88,13 @@ class lp_bound_propagator { hashtable m_visited_rows; hashtable m_visited_columns; u_map m_vertices; - vertex* m_root; + vertex* m_root = nullptr; // At some point we can find a row with a single vertex non fixed vertex // then we can fix the whole tree, // by adjusting the vertices offsets, so they become absolute. // If the tree is fixed then in addition to checking with the m_vals_to_verts // we are going to check with the m_fixed_var_tables. - const vertex* m_fixed_vertex; + const vertex* m_fixed_vertex = nullptr; explanation m_fixed_vertex_explanation; // a pair (o, j) belongs to m_vals_to_verts iff x[j] = x[m_root->column()] + o map, default_eq> m_vals_to_verts; @@ -111,19 +112,199 @@ class lp_bound_propagator { T& m_imp; vector m_ibounds; + + + + map, default_eq> m_val2fixed_row; + + void try_add_equation_with_internal_fixed_tables(unsigned r1, vertex const* v) { + SASSERT(m_fixed_vertex); + if (v != m_root) + return; + unsigned v1 = v->column(); + unsigned r2 = UINT_MAX; + if (!m_val2fixed_row.find(val(v1), r2) || r2 >= lp().row_count()) { + m_val2fixed_row.insert(val(v1), r1); + return; + } + unsigned v2, v3; + int polarity; + if (!is_tree_offset_row(r2, v2, v3, polarity) || !not_set(v3) || + is_int(v1) != is_int(v2) || val(v1) != val(v2)) { + m_val2fixed_row.insert(val(v1), r1); + return; + } + + explanation ex; + explain_fixed_in_row(r1, ex); + explain_fixed_in_row(r2, ex); + add_eq_on_columns(ex, v1, v2, true); + } + + void try_add_equation_with_lp_fixed_tables(unsigned row_index, const vertex *v) { + SASSERT(m_fixed_vertex); + unsigned v_j = v->column(); + unsigned j = null_lpvar; + if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) { + // try_add_equation_with_internal_fixed_tables(row_index, v); + return; + } + + TRACE("cheap_eq", + tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl; + tout << "v = "; print_vert(tout, v) << std::endl; + tout << "found j " << j << std::endl; lp().print_column_info(j, tout)<< std::endl; + tout << "found j = " << j << std::endl;); + vector path = connect_in_tree(v, m_fixed_vertex); + explanation ex = get_explanation_from_path(path); + ex.add_expl(m_fixed_vertex_explanation); + explain_fixed_column(j, ex); + add_eq_on_columns(ex, j, v_j, true); + } + + void try_add_equation_with_val_table(const vertex *v) { + SASSERT(m_fixed_vertex); + unsigned v_j = v->column(); + const vertex *u = nullptr; + if (!m_vals_to_verts.find(val(v_j), u)) { + m_vals_to_verts.insert(val(v_j), v); + return; + } + unsigned j = u->column(); + if (j == v_j || is_int(j) != is_int(v_j)) + return; + + TRACE("cheap_eq", tout << "found j=" << j << " for v="; + print_vert(tout, v) << "\n in m_vals_to_verts\n";); + vector path = connect_in_tree(u, v); + explanation ex = get_explanation_from_path(path); + ex.add_expl(m_fixed_vertex_explanation); + add_eq_on_columns(ex, j, v_j, true); + } + + static bool not_set(unsigned j) { return j == UINT_MAX; } + static bool is_set(unsigned j) { return j != UINT_MAX; } + + void create_root(unsigned row_index) { + SASSERT(!m_root && !m_fixed_vertex); + unsigned x, y; + int polarity; + TRACE("cheap_eq_det", print_row(tout, row_index);); + if (!is_tree_offset_row(row_index, x, y, polarity)) { + TRACE("cheap_eq_det", tout << "not an offset row\n";); + return; + } + TRACE("cheap_eq", print_row(tout, row_index);); + m_root = alloc_v(x); + set_polarity(m_root, 1); // keep m_root in the positive table + if (not_set(y)) { + set_fixed_vertex(m_root); + explain_fixed_in_row(row_index, m_fixed_vertex_explanation); + } + else { + vertex *v = add_child_with_check(row_index, y, m_root, polarity); + if (v) + explore_under(v); + } + explore_under(m_root); + } + + void explore_under(vertex * v) { + check_for_eq_and_add_to_val_tables(v); + go_over_vertex_column(v); + } + + // In case of only one non fixed column, and the function returns true, + // this column would be represened by x. + bool is_tree_offset_row(unsigned row_index, unsigned & x, unsigned & y, int & polarity) const { + x = y = UINT_MAX; + const row_cell* x_cell = nullptr; + const row_cell* y_cell = nullptr; + const auto & row = lp().get_row(row_index); + for (unsigned k = 0; k < row.size(); k++) { + const auto& c = row[k]; + if (column_is_fixed(c.var())) + continue; + if (not_set(x)) { + if (c.coeff().is_one() || c.coeff().is_minus_one()) { + x = c.var(); + x_cell = & c; + } + else + return false; + } + else if (not_set(y)) { + if (c.coeff().is_one() || c.coeff().is_minus_one()) { + y = c.var(); + y_cell = & c; + } + else + return false; + } + else + return false; + } + if (is_set(x)) { + if (is_set(y)) + polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1; + else + polarity = 1; + return true; + } + return false; + } + + void go_over_vertex_column(vertex * v) { + lpvar j = v->column(); + if (!check_insert(m_visited_columns, j)) + return; + + for (const auto & c : lp().get_column(j)) { + unsigned row_index = c.var(); + if (!check_insert(m_visited_rows, row_index)) + continue; + vertex* u = get_child_from_row(row_index, v); + if (u) + explore_under(u); + } + } + + void reset_cheap_eq_eh() { + if (!m_root) + return; + delete_tree(m_root); + m_root = nullptr; + set_fixed_vertex(nullptr); + m_fixed_vertex_explanation.clear(); + m_vals_to_verts.reset(); + m_vals_to_verts_neg.reset(); + m_pol.reset(); + m_vertices.reset(); + } + + struct reset_cheap_eq { + lp_bound_propagator& p; + reset_cheap_eq(lp_bound_propagator& p):p(p) {} + ~reset_cheap_eq() { p.reset_cheap_eq_eh(); } + }; + + public: + + lp_bound_propagator(T& imp): + m_imp(imp) {} + const vector& ibounds() const { return m_ibounds; } + void init() { m_improved_upper_bounds.clear(); m_improved_lower_bounds.clear(); m_ibounds.reset(); } - lp_bound_propagator(T& imp): m_root(nullptr), - m_fixed_vertex(nullptr), - m_imp(imp) {} - + const lar_solver& lp() const { return m_imp.lp(); } lar_solver& lp() { return m_imp.lp(); } + column_type get_column_type(unsigned j) const { return m_imp.lp().get_column_type(j); } @@ -133,9 +314,8 @@ public: } const mpq & get_lower_bound_rational(unsigned j) const { - return m_imp.lp().get_lower_bound(j).x; + return m_imp.lp().get_lower_bound(j).x; } - const impq & get_upper_bound(unsigned j) const { return m_imp.lp().get_upper_bound(j); @@ -167,19 +347,22 @@ public: found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); } - } else { + } + else { m_improved_lower_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); } - } else { // the upper bound case + } + else { // the upper bound case if (try_get_value(m_improved_upper_bounds, j, k)) { auto & found_bound = m_ibounds[k]; if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) { found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout);); } - } else { + } + else { m_improved_upper_bounds[j] = m_ibounds.size(); m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout);); @@ -199,54 +382,12 @@ public: return val(v->column()); } - void try_add_equation_with_lp_fixed_tables(const vertex *v) { - SASSERT(m_fixed_vertex); - unsigned v_j = v->column(); - unsigned j = null_lpvar; - if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) - return; - - TRACE("cheap_eq", tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;); - TRACE("cheap_eq", tout << "v = "; print_vert(tout, v) << std::endl;); - TRACE("cheap_eq", tout << "found j " << j << std::endl; - lp().print_column_info(j, tout)<< std::endl;); - TRACE("cheap_eq", tout << "found j = " << j << std::endl;); - vector path = connect_in_tree(v, m_fixed_vertex); - explanation ex = get_explanation_from_path(path); - ex.add_expl(m_fixed_vertex_explanation); - explain_fixed_column(j, ex); - add_eq_on_columns(ex, j, v->column()); - - } - - void try_add_equation_with_val_table(const vertex *v) { - SASSERT(m_fixed_vertex); - unsigned v_j = v->column(); - const vertex *u = nullptr; - if (!m_vals_to_verts.find(val(v_j), u)) { - m_vals_to_verts.insert(val(v_j), v); - return; - } - unsigned j = u->column(); - if (j == v_j || is_int(j) != is_int(v_j)) - return; - - TRACE("cheap_eq", tout << "found j=" << j << " for v="; - print_vert(tout, v) << "\n in m_vals_to_verts\n";); - vector path = connect_in_tree(u, v); - explanation ex = get_explanation_from_path(path); - ex.add_expl(m_fixed_vertex_explanation); - add_eq_on_columns(ex, j, v_j); - } - - bool tree_contains_r(vertex* root, vertex *v) const { if (*root == *v) return true; - for (auto e : root->edges()) { + for (auto e : root->edges()) if (tree_contains_r(e.target(), v)) return true; - } return false; } @@ -294,38 +435,12 @@ public: return v; } - static bool not_set(unsigned j) { return j == UINT_MAX; } - static bool is_set(unsigned j) { return j != UINT_MAX; } - - void create_root(unsigned row_index) { - SASSERT(!m_root && !m_fixed_vertex); - unsigned x, y; - int polarity; - TRACE("cheap_eq_det", print_row(tout, row_index);); - if (!is_tree_offset_row(row_index, x, y, polarity)) { - TRACE("cheap_eq_det", tout << "not an offset row\n";); - return; - } - TRACE("cheap_eq", print_row(tout, row_index);); - m_root = alloc_v(x); - set_polarity(m_root, 1); // keep m_root in the positive table - if (not_set(y)) { - set_fixed_vertex(m_root); - explain_fixed_in_row(row_index, m_fixed_vertex_explanation); - } else { - vertex *v = add_child_with_check(row_index, y, m_root, polarity); - if (v) - explore_under(v); - } - explore_under(m_root); - } unsigned column(unsigned row, unsigned index) { return lp().get_row(row)[index].var(); } bool fixed_phase() const { return m_fixed_vertex; } - // Returns the vertex to start exploration from, or nullptr. @@ -379,10 +494,12 @@ public: is_int(k->column()) == is_int(v->column()) && !is_equal(k->column(), v->column())) { report_eq(k, v); - } else { + } + else { TRACE("cheap_eq", tout << "no report\n";); } - } else { + } + else { TRACE("cheap_eq", tout << "registered: " << val(v) << " -> { "; print_vert(tout, v) << "} \n";); table.insert(val(v), v); } @@ -411,37 +528,31 @@ public: std::ostream& print_path(const vector& path, std::ostream& out) const { out << "path = \n"; - for (const edge& k : path) { + for (const edge& k : path) print_edge(k, out) << "\n"; - } return out; } - - - // we have v_i and v_j, indices of vertices at the same offsets void report_eq(const vertex* v_i, const vertex* v_j) { SASSERT(v_i != v_j); SASSERT(lp().get_column_value(v_i->column()) == lp().get_column_value(v_j->column())); TRACE("cheap_eq", tout << v_i->column() << " = " << v_j->column() << "\nu = "; - print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n"; - ); + print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n"); vector path = connect_in_tree(v_i, v_j); lp::explanation exp = get_explanation_from_path(path); - add_eq_on_columns(exp, v_i->column(), v_j->column()); + add_eq_on_columns(exp, v_i->column(), v_j->column(), false); } std::ostream& print_expl(std::ostream & out, const explanation& exp) const { - for (auto p : exp) { + for (auto p : exp) lp().constraints().display(out, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci()); - } return out; } - void add_eq_on_columns(const explanation& exp, lpvar j, lpvar k) { + bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) { SASSERT(j != k); unsigned je = lp().column_to_reported_index(j); unsigned ke = lp().column_to_reported_index(k); @@ -452,8 +563,10 @@ public: tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n"; ); - m_imp.add_eq(je, ke, exp); - lp().settings().stats().m_cheap_eqs++; + bool added = m_imp.add_eq(je, ke, exp, is_fixed); + if (added) + lp().stats().m_offset_eqs++; + return added; } // column to theory_var @@ -478,14 +591,10 @@ public: } void explain_fixed_in_row(unsigned row, explanation& ex) const { - TRACE("cheap_eq", - tout << lp().get_row(row) << std::endl; - ); - for (const auto & c : lp().get_row(row)) { - if (lp().is_fixed(c.var())) { + TRACE("cheap_eq", tout << lp().get_row(row) << std::endl); + for (const auto & c : lp().get_row(row)) + if (lp().is_fixed(c.var())) explain_fixed_column(c.var(), ex); - } - } } void explain_fixed_column(unsigned j, explanation & ex) const { @@ -536,10 +645,9 @@ public: if (visited_verts.find(v->column()) != visited_verts.end()) return false; visited_verts.insert(v->column()); - for (auto e : v->edges()) { + for (auto e : v->edges()) if (!tree_is_correct(e.target(), visited_verts)) return false; - } return true; } std::ostream& print_tree(std::ostream & out, vertex* v) const { @@ -553,43 +661,37 @@ public: return out; } - void try_add_equation_with_fixed_tables(const vertex* v) { - try_add_equation_with_lp_fixed_tables(v); + void try_add_equation_with_fixed_tables(unsigned row_index, const vertex* v) { + try_add_equation_with_lp_fixed_tables(row_index, v); try_add_equation_with_val_table(v); } - void create_fixed_eqs(const vertex* v) { - try_add_equation_with_fixed_tables(v); + void handle_fixed_phase(unsigned row_index) { + if (!fixed_phase()) + return; + const vertex* v = m_root; + try_add_equation_with_fixed_tables(row_index, v); for (auto e: v->edges()) - try_add_equation_with_fixed_tables(e.target()); + try_add_equation_with_fixed_tables(row_index, e.target()); } - void handle_fixed_phase() { - create_fixed_eqs(m_root); - } void cheap_eq_tree(unsigned row_index) { - TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";); - if (!check_insert(m_visited_rows, row_index)) - return; // already explored - create_root(row_index); - if (m_root == nullptr) { + reset_cheap_eq _reset(*this); + TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";); + if (!check_insert(m_visited_rows, row_index)) return; - } - TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";); + create_root(row_index); + if (!m_root) + return; + + TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";); SASSERT(tree_is_correct()); - if (fixed_phase()) - handle_fixed_phase(); - TRACE("cheap_eq", tout << "done for row_index " << row_index << "\n";); - TRACE("cheap_eq", tout << "tree size = " << verts_size();); - delete_tree(m_root); - m_root = nullptr; - set_fixed_vertex(nullptr); - m_fixed_vertex_explanation.clear(); - m_vals_to_verts.reset(); - m_vals_to_verts_neg.reset(); - m_pol.reset(); - m_vertices.reset(); + handle_fixed_phase(row_index); + + TRACE("cheap_eq", + tout << "done for row_index " << row_index << "\n"; + tout << "tree size = " << verts_size();); } std::ostream& print_row(std::ostream & out, unsigned row_index) const { @@ -643,71 +745,7 @@ public: return false; table.insert(j); return true; - } - - void go_over_vertex_column(vertex * v) { - lpvar j = v->column(); - if (!check_insert(m_visited_columns, j)) - return; - - for (const auto & c : lp().get_column(j)) { - unsigned row_index = c.var(); - if (!check_insert(m_visited_rows, row_index)) - continue; - vertex *u = get_child_from_row(row_index, v); - if (u) { - // debug - // if (verts_size() > 3) { - // std::cout << "big tree\n"; - // TRACE("cheap_eq", print_tree(tout, m_root);); - // exit(1); - // } // end debug - explore_under(u); - } - } - } - - void explore_under(vertex * v) { - check_for_eq_and_add_to_val_tables(v); - go_over_vertex_column(v); - } + } - // In case of only one non fixed column, and the function returns true, - // this column would be represened by x. - bool is_tree_offset_row( unsigned row_index, - unsigned & x, unsigned & y, int & polarity ) const { - x = y = UINT_MAX; - const row_cell* x_cell = nullptr; - const row_cell* y_cell = nullptr; - const auto & row = lp().get_row(row_index); - for (unsigned k = 0; k < row.size(); k++) { - const auto& c = row[k]; - if (column_is_fixed(c.var())) - continue; - if (not_set(x)) { - if (c.coeff().is_one() || c.coeff().is_minus_one()) { - x = c.var(); - x_cell = & c; - } else { - return false; - } - } else if (not_set(y)) { - if (c.coeff().is_one() || c.coeff().is_minus_one()) { - y = c.var(); - y_cell = & c; - } else - return false; - } else - return false; - } - if (is_set(x)) { - if (is_set(y)) - polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1; - else - polarity = 1; - return true; - } - return false; - } }; } diff --git a/src/math/lp/lp_settings.cpp b/src/math/lp/lp_settings.cpp index ce6edcfd5..592a98983 100644 --- a/src/math/lp/lp_settings.cpp +++ b/src/math/lp/lp_settings.cpp @@ -27,7 +27,7 @@ template bool lp::vectors_are_equal(vector const&, vectorget_expr(); expr* e2 = n2->get_expr(); - if (m.is_ite(e1) || m.is_ite(e2)) - return; + if (!is_fixed && !a.is_numeral(e1) && !a.is_numeral(e2) && (m.is_ite(e1) || m.is_ite(e2))) + return false; if (e1->get_sort() != e2->get_sort()) - return; + return false; reset_evidence(); for (auto ev : e) set_evidence(ev.ci(), m_core, m_eqs); auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2); ctx.propagate(n1, n2, jst->to_index()); + return true; } bool solver::bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const { diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index b449e049e..759c85fae 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -450,7 +450,7 @@ namespace arith { lp::lar_solver& lp() { return *m_solver; } lp::lar_solver const& lp() const { return *m_solver; } bool is_equal(theory_var x, theory_var y) const; - void add_eq(lpvar u, lpvar v, lp::explanation const& e); + bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed); void consume(rational const& v, lp::constraint_index j); bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const; }; diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index a5a42ee7a..4499752e5 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -262,22 +262,30 @@ namespace smt { } void context::display_eqc(std::ostream & out) const { - bool first = true; - for (enode * x : m_enodes) { - expr * n = x->get_expr(); - expr * r = x->get_root()->get_expr(); - if (n != r) { - if (first) { - out << "equivalence classes:\n"; - first = false; - } - out << "#" << n->get_id() << " -> #" << r->get_id() << ": "; - out << mk_pp(n, m) << " -> " << mk_pp(r, m) << "\n"; + if (m_enodes.empty()) + return; + unsigned count = 0; + for (enode * r : m_enodes) + if (r->is_root()) + ++count; + + out << "equivalence classes: " << count << "\n"; + for (enode * r : m_enodes) { + if (!r->is_root()) + continue; + out << "#" << enode_pp(r, *this) << "\n"; + if (r->get_class_size() == 1) + continue; + for (enode* n : *r) { + if (n != r) + out << " #" << enode_pp(n, *this) << "\n"; } } } void context::display_app_enode_map(std::ostream & out) const { + return; + // mainly useless if (!m_e_internalized_stack.empty()) { out << "expression -> enode:\n"; unsigned sz = m_e_internalized_stack.size(); diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index c47a7bcf7..500c47f5e 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -453,9 +453,6 @@ namespace smt { std::ostream& display_flat_app(std::ostream & out, app * n) const; - std::ostream& display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_expr()); } - - std::ostream& display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_expr()); } protected: void log_axiom_instantiation(app * r, unsigned axiom_id = UINT_MAX, unsigned num_bindings = 0, diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index f152c2430..4b57f043e 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -2239,17 +2239,17 @@ namespace smt { ctx.push_trail(value_trail(m_assume_eq_head)); while (m_assume_eq_head < m_assume_eq_candidates.size()) { - std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; - theory_var v1 = p.first; - theory_var v2 = p.second; + auto const& [v1, v2] = m_assume_eq_candidates[m_assume_eq_head]; + enode* n1 = get_enode(v1); + enode* n2 = get_enode(v2); m_assume_eq_head++; CTRACE("func_interp_bug", get_value(v1) == get_value(v2) && - get_enode(v1)->get_root() != get_enode(v2)->get_root(), - tout << "assuming eq: #" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); + n1->get_root() != n2->get_root(), + tout << "assuming eq: #" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n";); if (get_value(v1) == get_value(v2) && - get_enode(v1)->get_root() != get_enode(v2)->get_root() && - assume_eq(get_enode(v1), get_enode(v2))) { + n1->get_root() != n2->get_root() && + assume_eq(n1, n2)) { ++m_stats.m_assume_eqs; return true; } diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index a4dba46d0..049528f79 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -82,18 +82,17 @@ namespace smt { template void theory_arith::display_row(std::ostream & out, row const & r, bool compact) const { - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); + out << "(v" << r.get_base_var() << ") : "; bool first = true; - for (; it != end; ++it) { - if (!it->is_dead()) { + for (auto const& e : r) { + if (!e.is_dead()) { if (first) first = false; else out << " + "; - theory_var s = it->m_var; - numeral const & c = it->m_coeff; + theory_var s = e.m_var; + numeral const & c = e.m_coeff; if (!c.is_one()) out << c << "*"; if (compact) { @@ -103,7 +102,7 @@ namespace smt { } } else - display_var_flat_def(out, s); + out << enode_pp(get_enode(s), ctx); } } out << "\n"; @@ -117,20 +116,16 @@ namespace smt { else out << "rows (expanded view):\n"; unsigned num = m_rows.size(); - for (unsigned r_id = 0; r_id < num; r_id++) { - if (m_rows[r_id].m_base_var != null_theory_var) { + for (unsigned r_id = 0; r_id < num; r_id++) + if (m_rows[r_id].m_base_var != null_theory_var) display_row(out, r_id, compact); - } - } } template void theory_arith::display_row_shape(std::ostream & out, row const & r) const { - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - numeral const & c = it->m_coeff; + for (auto const& e : r) { + if (!e.is_dead()) { + numeral const & c = e.m_coeff; if (c.is_one()) out << "1"; else if (c.is_minus_one()) @@ -150,11 +145,9 @@ namespace smt { template bool theory_arith::is_one_minus_one_row(row const & r) const { - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - numeral const & c = it->m_coeff; + for (auto const& e : r) { + if (!e.is_dead()) { + numeral const & c = e.m_coeff; if (!c.is_one() && !c.is_minus_one()) return false; } @@ -184,11 +177,9 @@ namespace smt { for (unsigned r_id = 0; r_id < num; r_id++) { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - numeral const & c = it->m_coeff; + for (auto const& e : r) { + if (!e.is_dead()) { + numeral const & c = e.m_coeff; if (c.to_rational().is_big()) { std::string str = c.to_rational().to_string(); if (str.length() > 48) @@ -215,11 +206,9 @@ namespace smt { row const & r = m_rows[r_id]; if (r.m_base_var != null_theory_var) { num_rows++; - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - numeral const & c = it->m_coeff; + for (auto const& e : r) { + if (!e.is_dead()) { + numeral const & c = e.m_coeff; num_non_zeros++; if (c.is_one()) num_ones++; @@ -284,11 +273,9 @@ namespace smt { template void theory_arith::display_row_info(std::ostream & out, row const & r) const { display_row(out, r, true); - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) - if (!it->is_dead()) - display_var(out, it->m_var); + for (auto const& e : r) + if (!e.is_dead()) + display_var(out, e.m_var); } /** @@ -298,15 +285,14 @@ namespace smt { void theory_arith::display_simplified_row(std::ostream & out, row const & r) const { bool has_rat_coeff = false; numeral k; - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); + out << "(v" << r.get_base_var() << ") : "; bool first = true; - for (; it != end; ++it) { - if (it->is_dead()) + for (auto const& e : r) { + if (e.is_dead()) continue; - theory_var v = it->m_var; - numeral const & c = it->m_coeff; + theory_var v = e.m_var; + numeral const & c = e.m_coeff; if (is_fixed(v)) { k += c * lower_bound(v).get_rational(); continue; @@ -328,11 +314,9 @@ namespace smt { } out << "\n"; if (has_rat_coeff) { - typename vector::const_iterator it = r.begin_entries(); - typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) - if (!it->is_dead() && (is_base(it->m_var) || (!is_fixed(it->m_var) && (lower(it->m_var) || upper(it->m_var))))) - display_var(out, it->m_var); + for (auto const& e : r) + if (!e.is_dead() && (is_base(e.m_var) || (!is_fixed(e.m_var) && (lower(e.m_var) || upper(e.m_var))))) + display_var(out, e.m_var); } } @@ -385,8 +369,7 @@ namespace smt { out << ", shared: " << get_context().is_shared(get_enode(v)); out << ", unassigned: " << m_unassigned_atoms[v]; out << ", rel: " << get_context().is_relevant(get_enode(v)); - out << ", def: "; - display_var_flat_def(out, v); + out << ", def: " << enode_pp(get_enode(v), ctx); out << "\n"; } @@ -477,28 +460,17 @@ namespace smt { theory_var v = a->get_var(); inf_numeral const & k = a->get_k(); enode * e = get_enode(v); - if (show_sign) { - if (!a->is_true()) - out << "not "; - else - out << " "; - } + if (show_sign) + out << (a->is_true()?" ":"not "); out << "v"; out.width(3); out << std::left << v << " #"; out.width(3); out << e->get_owner_id(); out << std::right; - out << " "; - if (a->get_atom_kind() == A_LOWER) - out << ">="; - else - out << "<="; - out << " "; + out << " " << ((a->get_atom_kind() == A_LOWER)? ">=" : "<=") << " "; out.width(6); - out << k << " "; - display_var_flat_def(out, v); - out << "\n"; + out << k << " " << enode_pp(get_enode(v), ctx) << "\n"; } template diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 124461189..0abc2870d 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -173,8 +173,8 @@ class theory_lra::imp { unsigned_vector m_bounds_trail; unsigned m_asserted_qhead; - svector m_to_check; // rows that should be checked for theory propagation - + svector m_bv_to_propagate; // Boolean variables that can be propagated + svector > m_assume_eq_candidates; unsigned m_assume_eq_head; lp::u_set m_tmp_var_set; @@ -233,6 +233,7 @@ class theory_lra::imp { resource_limit m_resource_limit; lp_bounds m_new_bounds; symbol m_farkas; + vector m_bound_params; lp::lp_bound_propagator m_bp; context& ctx() const { return th.get_context(); } @@ -870,6 +871,10 @@ public: m_bound_terms(m), m_bound_predicate(m) { + m_bound_params.push_back(parameter(m_farkas)); + m_bound_params.push_back(parameter(rational(1))); + m_bound_params.push_back(parameter(rational(1))); + } ~imp() { @@ -1071,7 +1076,7 @@ public: lp().pop(num_scopes); // VERIFY(l_false != make_feasible()); m_new_bounds.reset(); - m_to_check.reset(); + m_bv_to_propagate.reset(); if (m_nla) m_nla->pop(num_scopes); TRACE("arith", tout << "num scopes: " << num_scopes << " new scope level: " << m_scopes.size() << "\n";); @@ -1493,29 +1498,24 @@ public: ctx().push_trail(value_trail(m_assume_eq_head)); while (m_assume_eq_head < m_assume_eq_candidates.size()) { - std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; - theory_var v1 = p.first; - theory_var v2 = p.second; + auto const [v1, v2] = m_assume_eq_candidates[m_assume_eq_head]; enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); m_assume_eq_head++; CTRACE("arith", is_eq(v1, v2) && n1->get_root() != n2->get_root(), tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { + if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) return true; - } } return false; } bool is_eq(theory_var v1, theory_var v2) { - if (use_nra_model()) { + if (use_nra_model()) return m_nla->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); - } - else { + else return get_ivalue(v1) == get_ivalue(v2); - } } bool has_delayed_constraints() const { @@ -1523,6 +1523,8 @@ public: } final_check_status final_check_eh() { + if (propagate_core()) + return FC_CONTINUE; m_model_is_initialized = false; IF_VERBOSE(12, verbose_stream() << "final-check " << lp().get_status() << "\n"); lbool is_sat = l_true; @@ -1534,9 +1536,7 @@ public: switch (is_sat) { case l_true: - TRACE("arith", display(tout); - /* ctx().display(tout);*/ - ); + TRACE("arith", display(tout)); switch (check_lia()) { case l_true: @@ -2048,41 +2048,59 @@ public: return false; } - bool m_new_def{ false }; + bool m_new_def = false ; + + bool adaptive() const { return ctx().get_fparams().m_arith_adaptive; } + double adaptive_assertion_threshold() const { return ctx().get_fparams().m_arith_adaptive_assertion_threshold; } + + bool process_atoms() const { + if (!adaptive()) + return true; + unsigned total_conflicts = ctx().get_num_conflicts(); + if (total_conflicts < 10) + return true; + double f = static_cast(m_num_conflicts)/static_cast(total_conflicts); + return f >= adaptive_assertion_threshold(); + } bool can_propagate() { + return process_atoms() && can_propagate_core(); + } + + bool can_propagate_core() { return m_asserted_atoms.size() > m_asserted_qhead || m_new_def; } - void propagate() { + bool propagate() { + return process_atoms() && propagate_core(); + } + + bool propagate_core() { m_model_is_initialized = false; flush_bound_axioms(); - if (!can_propagate()) { - return; - } - m_new_def = false; + if (!can_propagate_core()) + return false; + m_new_def = false; while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent() && m.inc()) { - bool_var bv = m_asserted_atoms[m_asserted_qhead].m_bv; - bool is_true = m_asserted_atoms[m_asserted_qhead].m_is_true; - m_to_check.push_back(bv); + auto [bv, is_true] = m_asserted_atoms[m_asserted_qhead]; + + m_bv_to_propagate.push_back(bv); + api_bound* b = nullptr; - TRACE("arith", tout << "propagate: " << literal(bv, !is_true) << "\n";); - if (m_bool_var2bound.find(bv, b)) { + TRACE("arith", tout << "propagate: " << literal(bv, !is_true) << "\n"; + if (!m_bool_var2bound.contains(bv)) tout << "not found\n"); + if (m_bool_var2bound.find(bv, b)) assert_bound(bv, is_true, *b); - } - else { - TRACE("arith", tout << "not found " << bv << "\n";); - } ++m_asserted_qhead; } if (ctx().inconsistent()) { - m_to_check.reset(); - return; + m_bv_to_propagate.reset(); + return true; } lbool lbl = make_feasible(); if (!m.inc()) - return; + return false; switch(lbl) { case l_false: @@ -2096,7 +2114,7 @@ public: case l_undef: break; } - + return true; } bool should_propagate() const { @@ -2246,26 +2264,28 @@ public: assign(bound, m_core, m_eqs, m_params); } - void add_eq(lpvar u, lpvar v, lp::explanation const& e) { + bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed) { if (ctx().inconsistent()) - return; + return false; theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form enode* n1 = get_enode(uv); enode* n2 = get_enode(vv); + TRACE("arith", tout << "add-eq " << mk_pp(n1->get_expr(), m) << " == " << mk_pp(n2->get_expr(), m) << " " << n1->get_expr_id() << " == " << n2->get_expr_id() << "\n";); if (n1->get_root() == n2->get_root()) - return; + return false; expr* e1 = n1->get_expr(); expr* e2 = n2->get_expr(); if (e1->get_sort() != e2->get_sort()) - return; - if (m.is_ite(e1) || m.is_ite(e2)) - return; + return false; + if (!is_fixed && !a.is_numeral(e1) && !a.is_numeral(e2) && (m.is_ite(e1) || m.is_ite(e2))) + return false; reset_evidence(); for (auto ev : e) set_evidence(ev.ci(), m_core, m_eqs); assign_eq(uv, vv); + return true; } literal_vector m_core2; @@ -2440,6 +2460,7 @@ public: typedef lp_bounds::iterator iterator; void flush_bound_axioms() { + CTRACE("arith", !m_new_bounds.empty(), tout << "flush bound axioms\n";); while (!m_new_bounds.empty()) { @@ -2458,7 +2479,7 @@ public: CTRACE("arith", atoms.size() > 1, for (auto* a : atoms) a->display(tout) << "\n";); lp_bounds occs(m_bounds[v]); - + std::sort(atoms.begin(), atoms.end(), compare_bounds()); std::sort(occs.begin(), occs.end(), compare_bounds()); @@ -2558,14 +2579,15 @@ public: } void propagate_basic_bounds() { - for (auto const& bv : m_to_check) { + for (auto const& bv : m_bv_to_propagate) { api_bound* b = nullptr; if (m_bool_var2bound.find(bv, b)) { propagate_bound(bv, ctx().get_assignment(bv) == l_true, *b); - if (ctx().inconsistent()) break; + if (ctx().inconsistent()) + break; } } - m_to_check.reset(); + m_bv_to_propagate.reset(); } // for glb lo': lo' < lo: @@ -2633,10 +2655,7 @@ public: ctx().display_literals_verbose(tout, m_core); ctx().display_literal_verbose(tout << " => ", lit2); tout << "\n";); - m_params.push_back(parameter(m_farkas)); - m_params.push_back(parameter(rational(1))); - m_params.push_back(parameter(rational(1))); - assign(lit2, m_core, m_eqs, m_params); + assign(lit2, m_core, m_eqs, m_bound_params); ++m_stats.m_bounds_propagations; } @@ -3194,7 +3213,7 @@ public: m_assume_eq_head = 0; m_scopes.reset(); m_stats.reset(); - m_to_check.reset(); + m_bv_to_propagate.reset(); m_model_is_initialized = false; } @@ -3661,7 +3680,7 @@ public: else if (can_get_value(v)) out << " = " << get_value(v); if (is_int(v)) out << ", int"; if (ctx().is_shared(get_enode(v))) out << ", shared"; - out << " := "; th.display_var_flat_def(out, v) << "\n"; + out << " := " << enode_pp(get_enode(v), ctx()) << "\n"; } } From 091079e58ca919b7c0e8fa768858c936991c1619 Mon Sep 17 00:00:00 2001 From: Clemens Eisenhofer <56730610+CEisenhofer@users.noreply.github.com> Date: Tue, 2 Nov 2021 23:03:02 +0100 Subject: [PATCH 84/89] Added user propagator example (#5625) * Added user propagator example * User propagator example code refactoring (+ removed unused parameter warning) * Moved user-propagator example to its own directory --- examples/CMakeLists.txt | 20 +- examples/userPropagator/CMakeLists.txt | 45 +++ examples/userPropagator/README | 10 + examples/userPropagator/example.cpp | 370 +++++++++++++++++++++++++ examples/userPropagator/example.pdf | Bin 0 -> 286105 bytes src/api/c++/z3++.h | 4 +- 6 files changed, 446 insertions(+), 3 deletions(-) create mode 100644 examples/userPropagator/CMakeLists.txt create mode 100644 examples/userPropagator/README create mode 100644 examples/userPropagator/example.cpp create mode 100644 examples/userPropagator/example.pdf diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c407da365..dcc21f255 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -106,6 +106,24 @@ ExternalProject_Add(z3_tptp5 ) set_target_properties(z3_tptp5 PROPERTIES EXCLUDE_FROM_ALL TRUE) +################################################################################ +# Build example user-propagator project using libz3's C++ API as an external project +################################################################################ +ExternalProject_Add(userPropagator + DEPENDS libz3 + # Configure step + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/userPropagator" + CMAKE_ARGS + "-DZ3_DIR=${PROJECT_BINARY_DIR}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" + # Build step + ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/userPropagator_build_dir" + # Install Step + INSTALL_COMMAND "${CMAKE_COMMAND}" -E echo "" # Dummy command + ) +set_target_properties(userPropagator PROPERTIES EXCLUDE_FROM_ALL TRUE) + ################################################################################ # Build Python examples ################################################################################ @@ -118,4 +136,4 @@ endif() ################################################################################ if (Z3_BUILD_DOTNET_BINDINGS) add_subdirectory(dotnet) -endif() \ No newline at end of file +endif() diff --git a/examples/userPropagator/CMakeLists.txt b/examples/userPropagator/CMakeLists.txt new file mode 100644 index 000000000..9ed916d46 --- /dev/null +++ b/examples/userPropagator/CMakeLists.txt @@ -0,0 +1,45 @@ +################################################################################ +# Example C++ project +################################################################################ +project(Z3_USER_PROPAGATOR_EXAMPLE CXX) +cmake_minimum_required(VERSION 3.4) +find_package(Z3 + REQUIRED + CONFIG + # `NO_DEFAULT_PATH` is set so that -DZ3_DIR has to be passed to find Z3. + # This should prevent us from accidentally picking up an installed + # copy of Z3. This is here to benefit Z3's build system when building + # this project. When making your own project you probably shouldn't + # use this option. + NO_DEFAULT_PATH +) + +################################################################################ +# Z3 C++ API bindings require C++11 +################################################################################ +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +message(STATUS "Z3_FOUND: ${Z3_FOUND}") +message(STATUS "Found Z3 ${Z3_VERSION_STRING}") +message(STATUS "Z3_DIR: ${Z3_DIR}") + +add_executable(user_propagator_example example.cpp) +target_include_directories(user_propagator_example PRIVATE ${Z3_CXX_INCLUDE_DIRS}) +target_link_libraries(user_propagator_example PRIVATE ${Z3_LIBRARIES}) + +if (CMAKE_SYSTEM_NAME MATCHES "[Ww]indows") + # On Windows we need to copy the Z3 libraries + # into the same directory as the executable + # so that they can be found. + foreach (z3_lib ${Z3_LIBRARIES}) + message(STATUS "Adding copy rule for ${z3_lib}") + add_custom_command(TARGET user_propagator_example + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy_if_different + $ + $ + ) + endforeach() +endif() diff --git a/examples/userPropagator/README b/examples/userPropagator/README new file mode 100644 index 000000000..780a334ef --- /dev/null +++ b/examples/userPropagator/README @@ -0,0 +1,10 @@ +Small example using the user-propagator. +To build the example execute + make examples +in the build directory. + +This command will create the executable user_propagator_example. +On Windows, you can just execute it. +On macOS and Linux, you must install z3 first using + sudo make install +OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. diff --git a/examples/userPropagator/example.cpp b/examples/userPropagator/example.cpp new file mode 100644 index 000000000..b66e3bb0e --- /dev/null +++ b/examples/userPropagator/example.cpp @@ -0,0 +1,370 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "z3++.h" + +/** + * The program solves the n-queens problem (number of solutions) with 4 different approaches + * 1) Bit-Vector constraints + Default solver + Blocking Clauses + * 2) Bit-Vector constraints + Simple solver + Blocking Clauses + * 3) Bit-Vector constraints + Simple solver + Adding contradictions in the propagator + * 4) Constraints only implicit via the propagator + Simple solver + Adding contradictions in the propagator + * + * Runs 1 + 2 are done for comparison with 3 + 4 + */ + +using namespace std::chrono; +using std::to_string; + +#define QUEEN +#define REPETITIONS 5 + +#define SIZE(x) std::extent::value + +#ifdef LOG +#define WriteEmptyLine std::cout << std::endl +#define WriteLine(x) std::cout << (x) << std::endl +#define Write(x) std::cout << x +#else +#define WriteEmptyLine +#define WriteLine(x) +#define Write(x) +#endif + +typedef std::vector model; + +struct model_hash_function { + std::size_t operator()(const model &m) const { + size_t hash = 0; + for (unsigned i = 0; i < m.size(); i++) { + hash *= m.size(); + hash += m[i]; + } + return hash; + } +}; + +class user_propagator : public z3::user_propagator_base { + +protected: + + unsigned board; + std::unordered_map& id_mapping; + model currentModel; + std::unordered_set modelSet; + std::vector fixedValues; + std::stack fixedCnt; + + int solutionId = 1; + +public: + + int getModelCount() const { + return solutionId - 1; + } + + void final() final { + this->conflict((unsigned) fixedValues.size(), fixedValues.data()); + if (modelSet.find(currentModel) != modelSet.end()) { + WriteLine("Got already computed model"); + return; + } + Write("Model #" << solutionId << ":\n"); + solutionId++; +#ifdef LOG + for (unsigned i = 0; i < fixedValues.size(); i++) { + unsigned id = fixedValues[i]; + WriteLine("q" + to_string(id_mapping[id]) + " = " + to_string(currentModel[id])); + } +#endif + modelSet.insert(currentModel); + WriteEmptyLine; + } + + static unsigned bvToInt(z3::expr e) { + return (unsigned)e.get_numeral_int(); + } + + void fixed(unsigned id, z3::expr const &e) override { + fixedValues.push_back(id); + unsigned value = bvToInt(e); + currentModel[id_mapping[id]] = value; + } + + user_propagator(z3::solver *s, std::unordered_map& idMapping, unsigned board) + : user_propagator_base(s), board(board), id_mapping(idMapping), currentModel(board, (unsigned)-1) { + + this->register_fixed(); + this->register_final(); + } + + virtual ~user_propagator() = default; + + void push() override { + fixedCnt.push((unsigned) fixedValues.size()); + } + + void pop(unsigned num_scopes) override { + for (unsigned i = 0; i < num_scopes; i++) { + unsigned lastCnt = fixedCnt.top(); + fixedCnt.pop(); + for (auto j = fixedValues.size(); j > lastCnt; j--) { + currentModel[fixedValues[j - 1]] = (unsigned)-1; + } + fixedValues.resize(lastCnt); + } + } + + user_propagator_base *fresh(Z3_context) override { return this; } +}; + +class user_propagator_with_theory : public user_propagator { + +public: + + void fixed(unsigned id, z3::expr const &e) override { + unsigned queenId = id_mapping[id]; + unsigned queenPos = bvToInt(e); + + if (queenPos >= board) { + this->conflict(1, &id); + return; + } + + for (unsigned fixed : fixedValues) { + unsigned otherId = id_mapping[fixed]; + unsigned otherPos = currentModel[fixed]; + + if (queenPos == otherPos) { + const unsigned conflicting[] = {id, fixed}; + this->conflict(2, conflicting); + continue; + } +#ifdef QUEEN + int diffY = abs((int)queenId - (int)otherId); + int diffX = abs((int)queenPos - (int)otherPos); + if (diffX == diffY) { + const unsigned conflicting[] = {id, fixed}; + this->conflict(2, conflicting); + } +#endif + } + + fixedValues.push_back(id); + currentModel[id_mapping[id]] = queenPos; + } + + user_propagator_with_theory(z3::solver *s, std::unordered_map& idMapping, unsigned board) + : user_propagator(s, idMapping, board) {} +}; + +int log2i(unsigned n) { + if (n <= 0) { + return 0; + } + if (n <= 2) { + return 1; + } + unsigned l = 1; + int i = 0; + while (l < n) { + l <<= 1; + i++; + } + return i; +} + +std::vector createQueens(z3::context &context, unsigned num) { + std::vector queens; + int bits = log2i(num) + 1 /*to detect potential overflow in the diagonal*/; + for (unsigned i = 0; i < num; i++) { + queens.push_back(context.bv_const((std::string("q") + to_string(i)).c_str(), bits)); + } + return queens; +} + +void createConstraints(z3::context &context, z3::solver &solver, const std::vector &queens) { + for (unsigned i = 0; i < queens.size(); i++) { + // assert column range + solver.add(z3::uge(queens[i], 0)); + solver.add(z3::ule(queens[i], (int) (queens.size() - 1))); + } + + z3::expr_vector distinct(context); + for (const z3::expr &queen : queens) { + distinct.push_back(queen); + } + + solver.add(z3::distinct(distinct)); + +#ifdef QUEEN + for (unsigned i = 0; i < queens.size(); i++) { + for (unsigned j = i + 1; j < queens.size(); j++) { + solver.add((int)(j - i) != (queens[j] - queens[i])); + solver.add((int)(j - i) != (queens[i] - queens[j])); + } + } +#endif +} + +int test01(unsigned num, bool simple) { + z3::context context; + z3::solver solver(context, !simple ? Z3_mk_solver(context) : Z3_mk_simple_solver(context)); + + std::vector queens = createQueens(context, num); + + createConstraints(context, solver, queens); + + int solutionId = 1; + + while (true) { + z3::check_result res = solver.check(); + + if (res != z3::check_result::sat) { + break; + } + + z3::model model = solver.get_model(); + + WriteLine("Model #" + to_string(solutionId) + ":"); + solutionId++; + + z3::expr_vector blocking(context); + + for (unsigned i = 0; i < num; i++) { + z3::expr eval = model.eval(queens[i]); + WriteLine(("q" + to_string(i) + " = " + to_string(eval.get_numeral_int()))); + blocking.push_back(queens[i] != eval); + } + + solver.add(z3::mk_or(blocking)); + + WriteEmptyLine; + } + return solutionId - 1; +} + +inline int test0(unsigned num) { + return test01(num, false); +} + +inline int test1(unsigned num) { + return test01(num, true); +} + +int test23(unsigned num, bool withTheory) { + z3::context context; + z3::solver solver(context, Z3_mk_simple_solver(context)); + std::unordered_map idMapping; + + user_propagator *propagator; + if (!withTheory) { + propagator = new user_propagator(&solver, idMapping, num); + } + else { + propagator = new user_propagator_with_theory(&solver, idMapping, num); + } + + std::vector queens = createQueens(context, num); + + for (unsigned i = 0; i < queens.size(); i++) { + unsigned id = propagator->add(queens[i]); + idMapping[id] = i; + } + + if (!withTheory) { + createConstraints(context, solver, queens); + } + + solver.check(); + int res = propagator->getModelCount(); + delete propagator; + return res; +} + +inline int test2(unsigned num) { + return test23(num, false); +} + +inline int test3(unsigned num) { + return test23(num, true); +} + +int main() { + + for (int num = 4; num <= 11; num++) { + + std::cout << "num = " << num << ":\n" << std::endl; + + unsigned seed = (unsigned) high_resolution_clock::now().time_since_epoch().count(); + const char *testName[] = + { + "BV + Blocking clauses (Default solver)", + "BV + Blocking clauses (Simple solver)", + "BV + Adding conflicts", + "Custom theory + conflicts", + }; + int permutation[4] = + { + 0, + 1, + 2, + 3, + }; + double timeResults[REPETITIONS * SIZE(permutation)]; + + for (int rep = 0; rep < REPETITIONS; rep++) { + // Execute strategies in a randomised order + std::shuffle(&permutation[0], &permutation[SIZE(permutation) - 1], std::default_random_engine(seed)); + + for (int i : permutation) { + int modelCount = -1; + + auto now1 = high_resolution_clock::now(); + + switch (i) { + case 0: + modelCount = test0(num); + break; + case 1: + modelCount = test1(num); + break; + case 2: + modelCount = test2(num); + break; + case 3: + modelCount = test3(num); + break; + default: + WriteLine("Unknown case"); + break; + } + auto now2 = high_resolution_clock::now(); + duration ms = now2 - now1; + std::cout << testName[i] << " took " << ms.count() << "ms (" << modelCount << " models)" << std::endl; + timeResults[rep * SIZE(permutation) + i] = ms.count(); + WriteLine("-------------"); + } + } + + std::cout << "\n" << std::endl; + + for (unsigned i = 0; i < SIZE(permutation); i++) { + std::cout << testName[i]; + double sum = 0; + for (int j = 0; j < REPETITIONS; j++) { + std::cout << " " << timeResults[j * SIZE(permutation) + i] << "ms"; + sum += timeResults[j * SIZE(permutation) + i]; + } + std::cout << " | avg: " << sum / REPETITIONS << "ms" << std::endl; + } + + std::cout << std::endl; + } +} diff --git a/examples/userPropagator/example.pdf b/examples/userPropagator/example.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2e802259cd2e80e0d78195f53c791cdc5936f3f5 GIT binary patch literal 286105 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/!5Qlm#D2(~&*zqVd0N30z8f+jVLY+0JFA2K}s@FsMFb%^i(77I# za0PD2fU#o^w$nG$B7=Xh!Y?&>`DDv#AF~bd2t!eoPNF91_P3;Tl$rO0@aqQ14D!Ie zfS4$7Th9B~=h1;M>Cy3JdFFtDpsRh(3zYiQHmXx&%zUbRQ^qng>RE%6m{d=wsXPD>D}ID-O(kf zu^%J&k~>v9gbpG)FHXvz)@pEQTY9Gys96$kKo4`O4I@(k(gg9^6AsE~3J4c_H!~{d zGNwSS_sV)6q)RLo=B1#_aWN+W_kepE8gH)^D_~gR%a

CAkHpM>DC#*J@|XOh+$Z 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 V!V~>zQ0Jggf Date: Wed, 3 Nov 2021 08:49:33 -0700 Subject: [PATCH 85/89] #5643 --- src/sat/smt/euf_model.cpp | 1 + src/smt/seq_eq_solver.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index f0f455f6c..21a02909e 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -46,6 +46,7 @@ namespace euf { value = r->get_expr(); else value = factory.get_fresh_value(srt); + (void)s; TRACE("model", tout << s.bpp(r) << " := " << value << "\n";); values.set(id, value); expr_ref_vector* vals = nullptr; diff --git a/src/smt/seq_eq_solver.cpp b/src/smt/seq_eq_solver.cpp index d53773f0d..9203c35bd 100644 --- a/src/smt/seq_eq_solver.cpp +++ b/src/smt/seq_eq_solver.cpp @@ -1145,7 +1145,7 @@ bool theory_seq::solve_nth_eq(expr_ref_vector const& ls, expr_ref_vector const& m.inc_ref(rhs); m.inc_ref(ls[0]); m_nth_eq2_cache.insert(std::make_pair(rhs, ls[0])); - ctx.push_trail(remove_obj_pair_map(m, m_nth_eq2_cache, rhs, ls[0])); + get_trail_stack().push(remove_obj_pair_map(m, m_nth_eq2_cache, rhs, ls[0])); ls1.push_back(s); if (!idx_is_zero) rs1.push_back(m_sk.mk_pre(s, idx)); rs1.push_back(m_util.str.mk_unit(rhs)); From af2cc460a96b6db97c774ef0b8926c145f26d216 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 3 Nov 2021 08:52:48 -0700 Subject: [PATCH 86/89] #5646 --- src/smt/theory_recfun.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/smt/theory_recfun.cpp b/src/smt/theory_recfun.cpp index 1a2bf0049..1bf987008 100644 --- a/src/smt/theory_recfun.cpp +++ b/src/smt/theory_recfun.cpp @@ -254,14 +254,16 @@ namespace smt { ctx.internalize_assertions(); lit = mk_literal(fn); } - else if (m.is_true(r) || m.is_false(r)) - std::swap(l, r); - else if (m.is_true(l)) - lit = mk_literal(r); - else if (m.is_false(l)) - lit = ~mk_literal(r); - else - lit = mk_eq(l, r, false); + else { + if (m.is_true(r) || m.is_false(r)) + std::swap(l, r); + if (m.is_true(l)) + lit = mk_literal(r); + else if (m.is_false(l)) + lit = ~mk_literal(r); + else + lit = mk_eq(l, r, false); + } ctx.mark_as_relevant(lit); return lit; } From f2fcbc7cb734e34138221f2599c541c566b49d60 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 7 Nov 2021 13:43:56 -0800 Subject: [PATCH 87/89] capture values not reference --- src/api/api_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 2837d6a36..659e4c4a4 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -875,7 +875,7 @@ extern "C" { init_solver(c, s); solver::push_eh_t _push = push_eh; solver::pop_eh_t _pop = pop_eh; - solver::fresh_eh_t _fresh = [&](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) { + solver::fresh_eh_t _fresh = [=](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) { ast_context_params params; params.set_foreign_manager(&m); auto* ctx = alloc(api::context, ¶ms, false); From 3a9656bc59a8f9ad26ffdcff931c833d974b0e94 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 7 Nov 2021 17:04:11 -0800 Subject: [PATCH 88/89] fixing issues with user propagator from python "fresh" remains broken (not working yet). --- src/api/python/z3/z3.py | 45 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 5c0d5765a..8e6a86cf1 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -11102,33 +11102,34 @@ class PropClosures: self.bases = {} self.lock = None - def set_threaded(): + def set_threaded(self): if self.lock is None: import threading - self.lock = threading.thread.Lock() + self.lock = threading.Lock() def get(self, ctx): if self.lock: - self.lock.acquire() - r = self.bases[ctx] - if self.lock: - self.lock.release() + with self.lock: + r = self.bases[ctx] + else: + r = self.bases[ctx] return r def set(self, ctx, r): if self.lock: - self.lock.acquire() - self.bases[ctx] = r - if self.lock: - self.lock.release() + with self.lock: + self.bases[ctx] = r + else: + self.bases[ctx] = r def insert(self, r): if self.lock: - self.lock.acquire() - id = len(self.bases) + 3 - self.bases[id] = r - if self.lock: - self.lock.release() + with self.lock: + id = len(self.bases) + 3 + self.bases[id] = r + else: + id = len(self.bases) + 3 + self.bases[id] = r return id @@ -11151,8 +11152,9 @@ def user_prop_pop(ctx, num_scopes): def user_prop_fresh(id, ctx): _prop_closures.set_threaded() - new_prop = UsePropagateBase(None, ctx) - _prop_closures.set(new_prop.id, new_prop.fresh()) + prop = _prop_closures.get(id) + new_prop = prop.fresh() + _prop_closures.set(new_prop.id, new_prop) return ctypes.c_void_p(new_prop.id) @@ -11214,11 +11216,12 @@ class UserPropagateBase: self.eq = None self.diseq = None if ctx: + # TBD fresh is broken: ctx is not of the right type when we reach here. self._ctx = Context() - Z3_del_context(self._ctx.ctx) - self._ctx.ctx = ctx - self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler) - Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT) + #Z3_del_context(self._ctx.ctx) + #self._ctx.ctx = ctx + #self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler) + #Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT) if s: Z3_solver_propagate_init(self.ctx_ref(), s.solver, From b5deba84263ebe3c9dc2aae73d7c11cafb572124 Mon Sep 17 00:00:00 2001 From: rainoftime <995204576@qq.com> Date: Wed, 10 Nov 2021 03:05:10 +0800 Subject: [PATCH 89/89] add EFSMT solving example (#5654) Co-authored-by: rainoftime --- examples/python/efsmt.py | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 examples/python/efsmt.py diff --git a/examples/python/efsmt.py b/examples/python/efsmt.py new file mode 100644 index 000000000..53c83c02e --- /dev/null +++ b/examples/python/efsmt.py @@ -0,0 +1,47 @@ +from z3 import * +from z3.z3util import get_vars + +''' +Modified from the example in pysmt +https://github.com/pysmt/pysmt/blob/97088bf3b0d64137c3099ef79a4e153b10ccfda7/examples/efsmt.py +''' +def efsmt(y, phi, maxloops=None): + """Solving exists x. forall y. phi(x, y)""" + vars = get_vars(phi) + x = [item for item in vars if item not in y] + esolver = Solver() + fsolver = Solver() + esolver.add(BoolVal(True)) + loops = 0 + while maxloops is None or loops <= maxloops: + loops += 1 + eres = esolver.check() + if eres == unsat: + return unsat + else: + emodel = esolver.model() + tau = [emodel.eval(var, True) for var in x] + sub_phi = phi + for i in range(len(x)): + sub_phi = simplify(substitute(sub_phi, (x[i], tau[i]))) + fsolver.add(Not(sub_phi)) + if fsolver.check() == sat: + fmodel = fsolver.model() + sigma = [fmodel.eval(v, True) for v in y] + sub_phi = phi + for j in range(len(y)): + sub_phi = simplify(substitute(sub_phi, (y[j], sigma[j]))) + esolver.add(sub_phi) + else: + return sat + return unknown + + +def test(): + x, y, z = Reals("x y z") + fmla = Implies(And(y > 0, y < 10), y - 2 * x < 7) + fmlb = And(y > 3, x == 1) + print(efsmt([y], fmla)) + print(efsmt([y], fmlb)) + +test()