From 284fcc2c044dd3d7c23dfe0e8bd6056a81885c0b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 20 Dec 2015 09:43:56 +0200 Subject: [PATCH 01/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 43 +++----- src/ast/rewriter/seq_rewriter.h | 4 +- src/ast/seq_decl_plugin.cpp | 2 +- src/ast/seq_decl_plugin.h | 2 +- src/smt/smt_setup.cpp | 2 +- src/smt/theory_seq.cpp | 176 +++++++++++++++++++++++++++--- src/smt/theory_seq.h | 139 ++++++++++++++++++++++- src/util/scoped_vector.h | 15 ++- 8 files changed, 334 insertions(+), 49 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 8a9ede35f..d7ac3ff67 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -156,17 +156,17 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { unsigned len = 0; unsigned j = 0; for (unsigned i = 0; i < m_es.size(); ++i) { - if (m_util.str.is_string(m_es[i], b)) { + if (m_util.str.is_string(m_es[i].get(), b)) { len += b.length(); } - else if (m_util.str.is_unit(m_es[i])) { + else if (m_util.str.is_unit(m_es[i].get())) { len += 1; } - else if (m_util.str.is_empty(m_es[i])) { + else if (m_util.str.is_empty(m_es[i].get())) { // skip } else { - m_es[j] = m_es[i]; + m_es[j] = m_es[i].get(); ++j; } } @@ -177,7 +177,7 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) { if (j != m_es.size() || j != 1) { expr_ref_vector es(m()); for (unsigned i = 0; i < j; ++i) { - es.push_back(m_util.str.mk_length(m_es[i])); + es.push_back(m_util.str.mk_length(m_es[i].get())); } if (len != 0) { es.push_back(m_autil.mk_numeral(rational(len, rational::ui64()), true)); @@ -207,14 +207,14 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { return BR_DONE; } // check if subsequence of b is in a. - ptr_vector as, bs; + expr_ref_vector as(m()), bs(m()); m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); bool found = false; for (unsigned i = 0; !found && i < as.size(); ++i) { if (bs.size() > as.size() - i) break; unsigned j = 0; - for (; j < bs.size() && as[j+i] == bs[j]; ++j) {}; + for (; j < bs.size() && as[j+i].get() == bs[j].get(); ++j) {}; found = j == bs.size(); } if (found) { @@ -292,7 +292,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { expr* b1 = m_util.str.get_leftmost_concat(b); isc1 = m_util.str.is_string(a1, s1); isc2 = m_util.str.is_string(b1, s2); - ptr_vector as, bs; + expr_ref_vector as(m()), bs(m()); if (a1 != b1 && isc1 && isc2) { if (s1.length() <= s2.length()) { @@ -342,7 +342,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); unsigned i = 0; - for (; i < as.size() && i < bs.size() && as[i] == bs[i]; ++i) {}; + for (; i < as.size() && i < bs.size() && as[i].get() == bs[i].get(); ++i) {}; if (i == as.size()) { result = m().mk_true(); return BR_DONE; @@ -350,7 +350,7 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { if (i == bs.size()) { expr_ref_vector es(m()); for (unsigned j = i; j < as.size(); ++j) { - es.push_back(m().mk_eq(m_util.str.mk_empty(m().get_sort(a)), as[j])); + es.push_back(m().mk_eq(m_util.str.mk_empty(m().get_sort(a)), as[j].get())); } result = mk_and(es); return BR_REWRITE3; @@ -522,7 +522,6 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve expr* a, *b; zstring s; bool change = false; - expr_ref_vector trail(m()); m_lhs.reset(); m_rhs.reset(); m_util.str.get_concat(l, m_lhs); @@ -545,7 +544,7 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve expr* r = m_rhs.back(); if (m_util.str.is_unit(r) && m_util.str.is_string(l)) { std::swap(l, r); - std::swap(m_lhs, m_rhs); + m_lhs.swap(m_rhs); } if (l == r) { m_lhs.pop_back(); @@ -575,7 +574,6 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve else { expr_ref s2(m_util.str.mk_string(s.extract(0, s.length()-2)), m()); m_rhs[m_rhs.size()-1] = s2; - trail.push_back(s2); } } else { @@ -587,10 +585,10 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve // solve from front unsigned head1 = 0, head2 = 0; while (true) { - while (head1 < m_lhs.size() && m_util.str.is_empty(m_lhs[head1])) { + while (head1 < m_lhs.size() && m_util.str.is_empty(m_lhs[head1].get())) { ++head1; } - while (head2 < m_rhs.size() && m_util.str.is_empty(m_rhs[head2])) { + while (head2 < m_rhs.size() && m_util.str.is_empty(m_rhs[head2].get())) { ++head2; } if (head1 == m_lhs.size() || head2 == m_rhs.size()) { @@ -598,11 +596,11 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve } SASSERT(head1 < m_lhs.size() && head2 < m_rhs.size()); - expr* l = m_lhs[head1]; - expr* r = m_rhs[head2]; + expr* l = m_lhs[head1].get(); + expr* r = m_rhs[head2].get(); if (m_util.str.is_unit(r) && m_util.str.is_string(l)) { std::swap(l, r); - std::swap(m_lhs, m_rhs); + m_lhs.swap(m_rhs); } if (l == r) { ++head1; @@ -631,7 +629,6 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve else { expr_ref s2(m_util.str.mk_string(s.extract(1, s.length()-1)), m()); m_rhs[m_rhs.size()-1] = s2; - trail.push_back(s2); } } else { @@ -643,8 +640,8 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve zstring s1, s2; while (head1 < m_lhs.size() && head2 < m_rhs.size() && - m_util.str.is_string(m_lhs[head1], s1) && - m_util.str.is_string(m_rhs[head2], s2)) { + m_util.str.is_string(m_lhs[head1].get(), s1) && + m_util.str.is_string(m_rhs[head2].get(), s2)) { unsigned l = std::min(s1.length(), s2.length()); for (unsigned i = 0; i < l; ++i) { if (s1[i] != s2[i]) { @@ -656,14 +653,12 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve } else { m_lhs[head1] = m_util.str.mk_string(s1.extract(l, s1.length()-l)); - trail.push_back(m_lhs[head1]); } if (l == s2.length()) { ++head2; } else { m_rhs[head2] = m_util.str.mk_string(s2.extract(l, s2.length()-l)); - trail.push_back(m_rhs[head2]); } change = true; } @@ -681,11 +676,9 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve m_rhs.pop_back(); if (l < s1.length()) { m_lhs.push_back(m_util.str.mk_string(s1.extract(0, s1.length()-l))); - trail.push_back(m_lhs.back()); } if (l < s2.length()) { m_rhs.push_back(m_util.str.mk_string(s2.extract(0, s2.length()-l))); - trail.push_back(m_rhs.back()); } change = true; } diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index de3634a51..c3e466585 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -32,7 +32,7 @@ Notes: class seq_rewriter { seq_util m_util; arith_util m_autil; - ptr_vector m_es, m_lhs, m_rhs; + expr_ref_vector m_es, m_lhs, m_rhs; br_status mk_seq_concat(expr* a, expr* b, expr_ref& result); br_status mk_seq_length(expr* a, expr_ref& result); @@ -63,7 +63,7 @@ class seq_rewriter { public: seq_rewriter(ast_manager & m, params_ref const & p = params_ref()): - m_util(m), m_autil(m) { + m_util(m), m_autil(m), m_es(m), m_lhs(m), m_rhs(m) { } ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index cda154050..75e27c081 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -626,7 +626,7 @@ bool seq_util::str::is_string(expr const* n, zstring& s) const { } -void seq_util::str::get_concat(expr* e, ptr_vector& es) const { +void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const { expr* e1, *e2; while (is_concat(e, e1, e2)) { get_concat(e1, es); diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 04161b08d..33d4de378 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -271,7 +271,7 @@ public: MATCH_BINARY(is_in_re); MATCH_UNARY(is_unit); - void get_concat(expr* e, ptr_vector& es) const; + void get_concat(expr* e, expr_ref_vector& es) const; expr* get_leftmost_concat(expr* e) const { expr* e1, *e2; while (is_concat(e, e1, e2)) e = e1; return e; } }; diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 8ebfa2d71..8a40f9d7a 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -815,7 +815,7 @@ namespace smt { } void setup::setup_seq() { - m_context.register_plugin(alloc(theory_seq_empty, m_manager)); + m_context.register_plugin(alloc(theory_seq, m_manager)); } void setup::setup_card() { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 641d5e444..753ddabd2 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -145,6 +145,7 @@ theory_seq::theory_seq(ast_manager& m): } theory_seq::~theory_seq() { + m_trail_stack.reset(); } @@ -157,6 +158,9 @@ final_check_status theory_seq::final_check_eh() { if (simplify_and_solve_eqs()) { return FC_CONTINUE; } + if (solve_nqs()) { + return FC_CONTINUE; + } if (ctx.inconsistent()) { return FC_CONTINUE; } @@ -209,7 +213,7 @@ bool theory_seq::check_ineqs() { bool theory_seq::branch_variable() { context& ctx = get_context(); unsigned sz = m_eqs.size(); - ptr_vector ls, rs; + expr_ref_vector ls(m), rs(m); for (unsigned i = 0; i < sz; ++i) { unsigned k = (i + m_branch_variable_head) % sz; eq e = m_eqs[k]; @@ -218,11 +222,11 @@ bool theory_seq::branch_variable() { m_util.str.get_concat(e.m_lhs, ls); m_util.str.get_concat(e.m_rhs, rs); - if (!ls.empty() && find_branch_candidate(ls[0], rs)) { + if (!ls.empty() && find_branch_candidate(ls[0].get(), rs)) { m_branch_variable_head = k; return true; } - if (!rs.empty() && find_branch_candidate(rs[0], ls)) { + if (!rs.empty() && find_branch_candidate(rs[0].get(), ls)) { m_branch_variable_head = k; return true; } @@ -230,7 +234,7 @@ bool theory_seq::branch_variable() { return false; } -bool theory_seq::find_branch_candidate(expr* l, ptr_vector const& rs) { +bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { TRACE("seq", tout << mk_pp(l, m) << " " << (is_var(l)?"var":"not var") << "\n";); @@ -434,8 +438,7 @@ bool theory_seq::simplify_eq(expr* l, expr* r, enode_pair_dependency* deps) { set_conflict(deps); return true; } - if (lhs.size() == 1 && l == lhs[0].get() && - rhs.size() == 1 && r == rhs[0].get()) { + if (unchanged(l, lhs) && unchanged(r, rhs)) { return false; } SASSERT(lhs.size() == rhs.size()); @@ -558,8 +561,115 @@ bool theory_seq::pre_process_eqs(bool simplify_or_solve) { return change; } +bool theory_seq::solve_nqs() { + bool change = false; + context & ctx = get_context(); + for (unsigned i = 0; !ctx.inconsistent() && i < m_nqs.size(); ++i) { + change = solve_ne(i) || change; + if (m_nqs[i].is_solved()) { + m_nqs.erase_and_swap(i); + --i; + } + } + return change; +} + +bool theory_seq::solve_ne(unsigned idx) { + context& ctx = get_context(); + seq_rewriter rw(m); + bool change = false; + ne const& n = m_nqs[idx]; + TRACE("seq", display_disequation(tout, n);); + + SASSERT(!n.is_solved()); + for (unsigned i = 0; i < n.m_lits.size(); ++i) { + switch (ctx.get_assignment(n.m_lits[i])) { + case l_true: + erase_lit(idx, i); + --i; + break; + case l_false: + // mark as solved in + mark_solved(idx); + return false; + case l_undef: + break; + } + } + for (unsigned i = 0; i < n.m_lhs.size(); ++i) { + expr_ref_vector lhs(m), rhs(m); + enode_pair_dependency* deps = 0; + expr* l = n.m_lhs[i]; + expr* r = n.m_rhs[i]; + expr_ref lh = canonize(l, deps); + expr_ref rh = canonize(r, deps); + if (!rw.reduce_eq(lh, rh, lhs, rhs)) { + mark_solved(idx); + return change; + } + else if (unchanged(l, lhs) && unchanged(r, rhs)) { + // continue + } + else if (unchanged(r, lhs) && unchanged(l, rhs)) { + // continue + } + else { + TRACE("seq", tout << lhs.size() << "\n"; + for (unsigned j = 0; j < lhs.size(); ++j) { + tout << mk_pp(lhs[j].get(), m) << " "; + } + tout << "\n"; + tout << mk_pp(l, m) << " != " << mk_pp(r, m) << "\n";); + + for (unsigned j = 0; j < lhs.size(); ++j) { + expr_ref nl(lhs[j].get(), m); + expr_ref nr(rhs[j].get(), m); + if (m_util.is_seq(nl) || m_util.is_re(nl)) { + //std::cout << "push_ne " << nl << " != " << nr << "\n"; + m_trail_stack.push(push_ne(*this, idx, nl, nr)); + } + else { + //std::cout << "push_lit\n"; + literal lit(mk_eq(nl, nr, false)); + m_trail_stack.push(push_lit(*this, idx, ~lit)); + ctx.mark_as_relevant(lit); + } + } + m_trail_stack.push(push_dep(*this, idx, deps)); + erase_index(idx, i); + --i; + } + } + if (n.m_lits.empty() && n.m_lhs.empty()) { + set_conflict(n.m_dep); + return true; + } + return change; +} + +void theory_seq::erase_lit(unsigned idx, unsigned i) { + ne const& n = m_nqs[idx]; + if (n.m_lits.size() < i + 1) { + m_trail_stack.push(set_lit(*this, idx, i, n.m_lits.back())); + } + m_trail_stack.push(pop_lit(*this, idx)); +} + +void theory_seq::mark_solved(unsigned idx) { + m_trail_stack.push(solved_ne(*this, idx)); +} + +void theory_seq::erase_index(unsigned idx, unsigned i) { + ne const& n = m_nqs[idx]; + unsigned sz = n.m_lhs.size(); + if (i + 1 != sz) { + m_trail_stack.push(set_ne(*this, idx, i, n.m_lhs[sz-1], n.m_rhs[sz-1])); + } + m_trail_stack.push(pop_ne(*this, idx)); +} + bool theory_seq::simplify_and_solve_eqs() { - context & ctx = get_context(); + context & ctx = get_context(); bool change = simplify_eqs(); while (!ctx.inconsistent() && solve_basic_eqs()) { simplify_eqs(); @@ -620,6 +730,7 @@ void theory_seq::apply_sort_cnstr(enode* n, sort* s) { void theory_seq::display(std::ostream & out) const { if (m_eqs.size() == 0 && + m_nqs.size() == 0 && m_ineqs.empty() && m_rep.empty() && m_exclude.empty()) { @@ -630,6 +741,10 @@ void theory_seq::display(std::ostream & out) const { out << "Equations:\n"; display_equations(out); } + if (m_nqs.size() > 0) { + out << "Disequations:\n"; + display_disequations(out); + } if (!m_ineqs.empty()) { out << "Negative constraints:\n"; for (unsigned i = 0; i < m_ineqs.size(); ++i) { @@ -654,6 +769,25 @@ void theory_seq::display_equations(std::ostream& out) const { } } +void theory_seq::display_disequations(std::ostream& out) const { + for (unsigned i = 0; i < m_nqs.size(); ++i) { + display_disequation(out, m_nqs[i]); + } +} + +void theory_seq::display_disequation(std::ostream& out, ne const& e) const { + for (unsigned j = 0; j < e.m_lits.size(); ++j) { + out << e.m_lits[j] << " "; + } + if (e.m_lits.size() > 0) { + out << "\n"; + } + for (unsigned j = 0; j < e.m_lhs.size(); ++j) { + out << mk_pp(e.m_lhs[j], m) << " != " << mk_pp(e.m_rhs[j], m) << "\n"; + } + display_deps(out, e.m_dep); +} + void theory_seq::display_deps(std::ostream& out, enode_pair_dependency* dep) const { vector _eqs; const_cast(m_dm).linearize(dep, _eqs); @@ -735,9 +869,6 @@ expr_ref theory_seq::expand(expr* e, enode_pair_dependency*& eqs) { else if (m_util.str.is_empty(e) || m_util.str.is_string(e)) { result = e; } - else if (m.is_eq(e, e1, e2)) { - result = m.mk_eq(expand(e1, deps), expand(e2, deps)); - } else if (m_util.str.is_prefix(e, e1, e2)) { result = m_util.str.mk_prefix(expand(e1, deps), expand(e2, deps)); } @@ -762,6 +893,9 @@ expr_ref theory_seq::expand(expr* e, enode_pair_dependency*& eqs) { else { result = e; } + if (result == e) { + deps = 0; + } expr_dep edr(result, deps); m_rep.add_cache(e, edr); eqs = m_dm.mk_join(eqs, deps); @@ -1164,6 +1298,13 @@ void theory_seq::assign_eq(bool_var v, bool is_true) { } } else { + //if (m_util.str.is_prefix(e, e1, e2)) { + // could add negative prefix axioms: + // len(e1) <= len(e2) => e2 = seq.prefix.left(e2)*seq.prefix.right(e2) + // & len(seq.prefix.left(e2)) = len(e1) + // & seq.prefix.left(e2) != e1 + // or could solve prefix/suffix disunification constraints. + //} m_trail_stack.push(push_back_vector(m_ineqs)); m_ineqs.push_back(e); } @@ -1181,15 +1322,15 @@ void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { } void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { - expr* e1 = get_enode(v1)->get_owner(); - expr* e2 = get_enode(v2)->get_owner(); - m_trail_stack.push(push_back_vector(m_ineqs)); - m_ineqs.push_back(mk_eq_atom(e1, e2)); + enode* n1 = get_enode(v1); + enode* n2 = get_enode(v2); + expr_ref e1(n1->get_owner(), m); + expr_ref e2(n2->get_owner(), m); + m_nqs.push_back(ne(e1, e2, m_dm.mk_leaf(enode_pair(n1, n2)))); m_exclude.update(e1, e2); } void theory_seq::push_scope_eh() { - TRACE("seq", tout << "push " << m_eqs.size() << "\n";); theory::push_scope_eh(); m_rep.push_scope(); m_exclude.push_scope(); @@ -1197,16 +1338,17 @@ void theory_seq::push_scope_eh() { m_trail_stack.push_scope(); m_trail_stack.push(value_trail(m_axioms_head)); m_eqs.push_scope(); + m_nqs.push_scope(); } void theory_seq::pop_scope_eh(unsigned num_scopes) { - TRACE("seq", tout << "pop " << m_eqs.size() << "\n";); m_trail_stack.pop_scope(num_scopes); theory::pop_scope_eh(num_scopes); m_dm.pop_scope(num_scopes); m_rep.pop_scope(num_scopes); m_exclude.pop_scope(num_scopes); - m_eqs.pop_scopes(num_scopes); + m_eqs.pop_scope(num_scopes); + m_nqs.pop_scope(num_scopes); } void theory_seq::restart_eh() { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index b2b45e77e..59ae5095a 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -104,6 +104,136 @@ namespace smt { eq& operator=(eq const& other) { m_lhs = other.m_lhs; m_rhs = other.m_rhs; m_dep = other.m_dep; return *this; } }; + + // asserted or derived disqequality with dependencies + struct ne { + bool m_solved; + expr_ref_vector m_lhs; + expr_ref_vector m_rhs; + literal_vector m_lits; + enode_pair_dependency* m_dep; + ne(expr_ref& l, expr_ref& r, enode_pair_dependency* d): + m_solved(false), m_lhs(l.get_manager()), m_rhs(r.get_manager()), m_dep(d) { + m_lhs.push_back(l); + m_rhs.push_back(r); + } + ne(ne const& other): + m_solved(other.m_solved), m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_lits(other.m_lits), m_dep(other.m_dep) {} + ne& operator=(ne const& other) { + m_solved = other.m_solved; + m_lhs.reset(); m_lhs.append(other.m_lhs); + m_rhs.reset(); m_rhs.append(other.m_rhs); + m_lits.reset(); m_lits.append(other.m_lits); + m_dep = other.m_dep; + return *this; + } + bool is_solved() const { return m_solved; } + }; + + class pop_lit : public trail { + unsigned m_idx; + literal m_lit; + public: + pop_lit(theory_seq& th, unsigned idx): m_idx(idx), m_lit(th.m_nqs[idx].m_lits.back()) { + th.m_nqs.ref(m_idx).m_lits.pop_back(); + } + virtual void undo(theory_seq & th) { th.m_nqs.ref(m_idx).m_lits.push_back(m_lit); } + }; + class push_lit : public trail { + unsigned m_idx; + public: + push_lit(theory_seq& th, unsigned idx, literal lit): m_idx(idx) { + th.m_nqs.ref(m_idx).m_lits.push_back(lit); + } + virtual void undo(theory_seq & th) { th.m_nqs.ref(m_idx).m_lits.pop_back(); } + }; + class set_lit : public trail { + unsigned m_idx; + unsigned m_i; + literal m_lit; + public: + set_lit(theory_seq& th, unsigned idx, unsigned i, literal lit): + m_idx(idx), m_i(i), m_lit(th.m_nqs[idx].m_lits[i]) { + th.m_nqs.ref(m_idx).m_lits[i] = lit; + } + virtual void undo(theory_seq & th) { th.m_nqs.ref(m_idx).m_lits[m_i] = m_lit; } + }; + void erase_lit(unsigned idx, unsigned i); + + class solved_ne : public trail { + unsigned m_idx; + public: + solved_ne(theory_seq& th, unsigned idx) : m_idx(idx) { th.m_nqs.ref(idx).m_solved = true; } + virtual void undo(theory_seq& th) { th.m_nqs.ref(m_idx).m_solved = false; } + }; + void mark_solved(unsigned idx); + + class push_ne : public trail { + unsigned m_idx; + public: + push_ne(theory_seq& th, unsigned idx, expr* l, expr* r) : m_idx(idx) { + th.m_nqs.ref(m_idx).m_lhs.push_back(l); + th.m_nqs.ref(m_idx).m_rhs.push_back(r); + } + virtual void undo(theory_seq& th) { th.m_nqs.ref(m_idx).m_lhs.pop_back(); th.m_nqs.ref(m_idx).m_rhs.pop_back(); } + }; + + class pop_ne : public trail { + expr_ref m_lhs; + expr_ref m_rhs; + unsigned m_idx; + public: + pop_ne(theory_seq& th, unsigned idx): + m_lhs(th.m_nqs[idx].m_lhs.back(), th.m), + m_rhs(th.m_nqs[idx].m_rhs.back(), th.m), + m_idx(idx) { + th.m_nqs.ref(idx).m_lhs.pop_back(); + th.m_nqs.ref(idx).m_rhs.pop_back(); + } + virtual void undo(theory_seq& th) { + th.m_nqs.ref(m_idx).m_lhs.push_back(m_lhs); + th.m_nqs.ref(m_idx).m_rhs.push_back(m_rhs); + m_lhs.reset(); + m_rhs.reset(); + } + }; + + class set_ne : public trail { + expr_ref m_lhs; + expr_ref m_rhs; + unsigned m_idx; + unsigned m_i; + public: + set_ne(theory_seq& th, unsigned idx, unsigned i, expr* l, expr* r): + m_lhs(th.m_nqs[idx].m_lhs[i], th.m), + m_rhs(th.m_nqs[idx].m_rhs[i], th.m), + m_idx(idx), + m_i(i) { + th.m_nqs.ref(idx).m_lhs[i] = l; + th.m_nqs.ref(idx).m_rhs[i] = r; + } + virtual void undo(theory_seq& th) { + th.m_nqs.ref(m_idx).m_lhs[m_i] = m_lhs; + th.m_nqs.ref(m_idx).m_rhs[m_i] = m_rhs; + m_lhs.reset(); + m_rhs.reset(); + } + }; + + class push_dep : public trail { + enode_pair_dependency* m_dep; + unsigned m_idx; + public: + push_dep(theory_seq& th, unsigned idx, enode_pair_dependency* d): m_dep(th.m_nqs[idx].m_dep), m_idx(idx) { + th.m_nqs.ref(idx).m_dep = d; + } + virtual void undo(theory_seq& th) { + th.m_nqs.ref(m_idx).m_dep = m_dep; + } + }; + + void erase_index(unsigned idx, unsigned i); + struct stats { stats() { reset(); } void reset() { memset(this, 0, sizeof(stats)); } @@ -114,6 +244,7 @@ namespace smt { enode_pair_dependency_manager m_dm; solution_map m_rep; // unification representative. scoped_vector m_eqs; // set of current equations. + scoped_vector m_nqs; // set of current disequalities. seq_factory* m_factory; // value factory expr_ref_vector m_ineqs; // inequalities to check solution against @@ -174,13 +305,17 @@ namespace smt { bool solve_unit_eq(expr* l, expr* r, enode_pair_dependency* dep); bool solve_basic_eqs(); + bool solve_nqs(); + bool solve_ne(unsigned i); + bool unchanged(expr* e, expr_ref_vector& es) const { return es.size() == 1 && es[0] == e; } + // asserting consequences void propagate_lit(enode_pair_dependency* dep, literal lit); void propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2); void propagate_eq(bool_var v, expr* e1, expr* e2); void set_conflict(enode_pair_dependency* dep); - bool find_branch_candidate(expr* l, ptr_vector const& rs); + bool find_branch_candidate(expr* l, expr_ref_vector const& rs); bool assume_equality(expr* l, expr* r); // variable solving utilities @@ -219,6 +354,8 @@ namespace smt { // diagnostics void display_equations(std::ostream& out) const; + void display_disequations(std::ostream& out) const; + void display_disequation(std::ostream& out, ne const& e) const; void display_deps(std::ostream& out, enode_pair_dependency* deps) const; public: theory_seq(ast_manager& m); diff --git a/src/util/scoped_vector.h b/src/util/scoped_vector.h index 917ecf2ab..a05b19487 100644 --- a/src/util/scoped_vector.h +++ b/src/util/scoped_vector.h @@ -46,7 +46,7 @@ public: m_elems_lim.push_back(m_elems_start); } - void pop_scopes(unsigned num_scopes) { + void pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; unsigned new_size = m_sizes.size() - num_scopes; unsigned src_lim = m_src_lim[new_size]; @@ -72,6 +72,12 @@ public: return m_elems[m_index[idx]]; } + // breaks abstraction, caller must ensure backtracking. + T& ref(unsigned idx) { + SASSERT(idx < m_size); + return m_elems[m_index[idx]]; + } + void set(unsigned idx, T const& t) { SASSERT(idx < m_size); unsigned n = m_index[idx]; @@ -102,6 +108,13 @@ public: SASSERT(invariant()); } + void erase_and_swap(unsigned i) { + if (i + 1 < size()) { + set(i, m_elems[m_index[i]]); + } + pop_back(); + } + unsigned size() const { return m_size; } bool empty() const { return m_size == 0; } From 8e26c977824a0246617eb55a0d2f252f6a13624c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 Dec 2015 13:09:03 +0200 Subject: [PATCH 02/35] tuning bit-vector operations Signed-off-by: Nikolaj Bjorner --- src/api/api_model.cpp | 4 +- .../rewriter/bit_blaster/bit_blaster_tpl.h | 5 +- .../bit_blaster/bit_blaster_tpl_def.h | 117 ++++++++++++----- src/ast/rewriter/bv_rewriter.cpp | 118 +++++++++++++----- src/ast/rewriter/bv_rewriter.h | 4 + src/smt/theory_bv.cpp | 45 ++++++- src/smt/theory_bv.h | 5 +- src/tactic/bv/bit_blaster_tactic.h | 47 +++---- src/tactic/core/solve_eqs_tactic.cpp | 20 +-- 9 files changed, 271 insertions(+), 94 deletions(-) diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 370e639b9..63092b2cf 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -277,7 +277,9 @@ extern "C" { RESET_ERROR_CODE(); CHECK_NON_NULL(f, 0); expr * e = to_func_interp_ref(f)->get_else(); - mk_c(c)->save_ast_trail(e); + if (e) { + mk_c(c)->save_ast_trail(e); + } RETURN_Z3(of_expr(e)); Z3_CATCH_RETURN(0); } diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h index ea8209c61..b812de941 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl.h @@ -121,8 +121,11 @@ public: void mk_comp(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); void mk_carry_save_adder(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr * const * c_bits, expr_ref_vector & sum_bits, expr_ref_vector & carry_bits); - void mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); + bool mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); + bool mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits); + void mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer& a_bits, ptr_buffer& b_bits, expr_ref_vector & out_bits); + bool is_bool_const(expr* e) const { return m().is_true(e) || m().is_false(e); } void mk_abs(unsigned sz, expr * const * a_bits, expr_ref_vector & out_bits); }; diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index 38a608f5f..942f97e93 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -38,7 +38,7 @@ void bit_blaster_tpl::checkpoint() { template bool bit_blaster_tpl::is_numeral(unsigned sz, expr * const * bits) const { for (unsigned i = 0; i < sz; i++) - if (!m().is_true(bits[i]) && !m().is_false(bits[i])) + if (!is_bool_const(bits[i])) return false; return true; } @@ -158,30 +158,24 @@ void bit_blaster_tpl::mk_subtracter(unsigned sz, expr * const * a_bits, exp template void bit_blaster_tpl::mk_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { SASSERT(sz > 0); - - if (!m_use_bcm) { - numeral n_a, n_b; - if (is_numeral(sz, a_bits, n_b)) - std::swap(a_bits, b_bits); - if (is_minus_one(sz, b_bits)) { - mk_neg(sz, a_bits, out_bits); - return; - } - if (is_numeral(sz, a_bits, n_a)) { - n_a *= n_b; - num2bits(n_a, sz, out_bits); - return; - } + numeral n_a, n_b; + if (is_numeral(sz, a_bits, n_b)) + std::swap(a_bits, b_bits); + if (is_minus_one(sz, b_bits)) { + mk_neg(sz, a_bits, out_bits); + return; } - else { - numeral n_a, n_b; - if (is_numeral(sz, a_bits, n_a)) { - mk_const_multiplier(sz, a_bits, b_bits, out_bits); - return; - } else if (is_numeral(sz, b_bits, n_b)) { - mk_const_multiplier(sz, b_bits, a_bits, out_bits); - return; - } + if (is_numeral(sz, a_bits, n_a)) { + n_a *= n_b; + num2bits(n_a, sz, out_bits); + return; + } + + if (mk_const_multiplier(sz, a_bits, b_bits, out_bits)) { + return; + } + if (mk_const_multiplier(sz, b_bits, a_bits, out_bits)) { + return; } if (!m_use_wtm) { @@ -1171,13 +1165,74 @@ void bit_blaster_tpl::mk_carry_save_adder(unsigned sz, expr * const * a_bit } template -void bit_blaster_tpl::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { - DEBUG_CODE({ - numeral x; - SASSERT(is_numeral(sz, a_bits, x)); - SASSERT(out_bits.empty()); - }); +bool bit_blaster_tpl::mk_const_case_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { + unsigned nb = 0; + unsigned case_size = 1; + unsigned circuit_size = sz*sz*5; + for (unsigned i = 0; case_size < circuit_size && i < sz; ++i) { + if (!is_bool_const(a_bits[i])) { + case_size *= 2; + } + if (!is_bool_const(b_bits[i])) { + case_size *= 2; + } + } + if (case_size >= circuit_size) { + return false; + } + SASSERT(out_bits.empty()); + ptr_buffer na_bits; + na_bits.append(sz, a_bits); + ptr_buffer nb_bits; + nb_bits.append(sz, b_bits); + mk_const_case_multiplier(true, 0, sz, na_bits, nb_bits, out_bits); + return false; +} + +template +void bit_blaster_tpl::mk_const_case_multiplier(bool is_a, unsigned i, unsigned sz, ptr_buffer& a_bits, ptr_buffer& b_bits, expr_ref_vector & out_bits) { + while (is_a && i < sz && is_bool_const(a_bits[i])) ++i; + if (is_a && i == sz) { is_a = false; i = 0; } + while (!is_a && i < sz && is_bool_const(b_bits[i])) ++i; + if (i < sz) { + expr_ref_vector out1(m()), out2(m()); + expr_ref x(m()); + x = is_a?a_bits[i]:b_bits[i]; + if (is_a) a_bits[i] = m().mk_true(); else b_bits[i] = m().mk_true(); + mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out1); + if (is_a) a_bits[i] = m().mk_false(); else b_bits[i] = m().mk_false(); + mk_const_case_multiplier(is_a, i+1, sz, a_bits, b_bits, out2); + if (is_a) a_bits[i] = x; else b_bits[i] = x; + SASSERT(out_bits.empty()); + for (unsigned j = 0; j < sz; ++j) { + out_bits.push_back(m().mk_ite(x, out1[j].get(), out2[j].get())); + } + } + else { + numeral n_a, n_b; + SASSERT(i == sz && !is_a); + VERIFY(is_numeral(sz, a_bits.c_ptr(), n_a)); + VERIFY(is_numeral(sz, b_bits.c_ptr(), n_b)); + n_a *= n_b; + num2bits(n_a, sz, out_bits); + } +} + +template +bool bit_blaster_tpl::mk_const_multiplier(unsigned sz, expr * const * a_bits, expr * const * b_bits, expr_ref_vector & out_bits) { + numeral n_a; + if (!is_numeral(sz, a_bits, n_a)) { + return false; + } + SASSERT(out_bits.empty()); + + if (mk_const_case_multiplier(sz, a_bits, b_bits, out_bits)) { + return true; + } + if (!m_use_bcm) { + return false; + } expr_ref_vector minus_b_bits(m()), tmp(m()); mk_neg(sz, b_bits, minus_b_bits); @@ -1255,4 +1310,6 @@ void bit_blaster_tpl::mk_const_multiplier(unsigned sz, expr * const * a_bit TRACE("bit_blaster_tpl_booth", for (unsigned i=0; iget_num_args(); + expr_ref t1(m()), t2(m()); + t1 = to_app(rhs)->get_arg(0); + if (sz > 2) { + t2 = m().mk_app(get_fid(), OP_BADD, sz-1, to_app(rhs)->get_args()+1); + } + else { + SASSERT(sz == 2); + t2 = to_app(rhs)->get_arg(1); + } + mk_t1_add_t2_eq_c(t1, t2, lhs, result); + return true; +} + +bool bv_rewriter::is_add_mul_const(expr* e) const { + if (!m_util.is_bv_add(e)) { + return false; + } + unsigned num = to_app(e)->get_num_args(); + for (unsigned i = 0; i < num; i++) { + expr * arg = to_app(e)->get_arg(i); + expr * c2, * x2; + if (m_util.is_numeral(arg)) + continue; + if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2)) + continue; + return false; + } + return true; +} + +bool bv_rewriter::is_concat_target(expr* lhs, expr* rhs) const { + return + m_util.is_concat(lhs) && (is_concat_split_target(rhs) || has_numeral(to_app(lhs))) || + m_util.is_concat(rhs) && (is_concat_split_target(lhs) || has_numeral(to_app(rhs))); +} + +bool bv_rewriter::has_numeral(app* a) const { + for (unsigned i = 0; i < a->get_num_args(); ++i) { + if (is_numeral(a->get_arg(i))) { + return true; + } + } + return false; +} + br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { + expr * c, * x; numeral c_val, c_inv_val; unsigned sz; @@ -2001,24 +2057,30 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { // c * x = t_1 + ... + t_n // and t_i's have non-unary coefficients (this condition is used to make sure we are actually reducing the number of multipliers). - if (m_util.is_bv_add(rhs)) { + if (is_add_mul_const(rhs)) { // Potential problem: this simplification may increase the number of adders by reducing the amount of sharing. - unsigned num = to_app(rhs)->get_num_args(); - unsigned i; - for (i = 0; i < num; i++) { - expr * arg = to_app(rhs)->get_arg(i); - expr * c2, * x2; - if (m_util.is_numeral(arg)) - continue; - if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2)) - continue; - break; - } - if (i == num) { - result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs)); - return BR_REWRITE2; + result = m().mk_eq(x, m_util.mk_bv_mul(m_util.mk_numeral(c_inv_val, sz), rhs)); + return BR_REWRITE2; + } + } + if (m_util.is_numeral(lhs, c_val, sz) && is_add_mul_const(rhs)) { + unsigned sz = to_app(rhs)->get_num_args(); + unsigned i = 0; + expr* c2, *x2; + numeral c2_val, c2_inv_val; + bool found = false; + for (; !found && i < sz; ++i) { + expr* arg = to_app(rhs)->get_arg(i); + if (m_util.is_bv_mul(arg, c2, x2) && m_util.is_numeral(c2, c2_val, sz) && + m_util.mult_inverse(c2_val, sz, c2_inv_val)) { + found = true; } } + if (found) { + result = m().mk_eq(m_util.mk_numeral(c2_inv_val*c_val, sz), + m_util.mk_bv_mul(m_util.mk_numeral(c2_inv_val, sz), rhs)); + return BR_REWRITE3; + } } return BR_FAILED; } @@ -2065,9 +2127,10 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { return st; } + expr_ref new_lhs(m()); + expr_ref new_rhs(m()); + if (m_util.is_bv_add(lhs) || m_util.is_bv_mul(lhs) || m_util.is_bv_add(rhs) || m_util.is_bv_mul(rhs)) { - expr_ref new_lhs(m()); - expr_ref new_rhs(m()); st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs); if (st != BR_FAILED) { if (is_numeral(new_lhs) && is_numeral(new_rhs)) { @@ -2080,28 +2143,27 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { new_rhs = rhs; } + lhs = new_lhs; + rhs = new_rhs; // Try to rewrite t1 + t2 = c --> t1 = c - t2 // Reason: it is much cheaper to bit-blast. - expr * t1, * t2; - if (m_util.is_bv_add(new_lhs, t1, t2) && is_numeral(new_rhs)) { - mk_t1_add_t2_eq_c(t1, t2, new_rhs, result); + if (isolate_term(lhs, rhs, result)) { return BR_REWRITE2; } - if (m_util.is_bv_add(new_rhs, t1, t2) && is_numeral(new_lhs)) { - mk_t1_add_t2_eq_c(t1, t2, new_lhs, result); - return BR_REWRITE2; + if (is_concat_target(lhs, rhs)) { + return mk_eq_concat(lhs, rhs, result); } - + if (st != BR_FAILED) { - result = m().mk_eq(new_lhs, new_rhs); + result = m().mk_eq(lhs, rhs); return BR_DONE; } } - if ((m_util.is_concat(lhs) && is_concat_split_target(rhs)) || - (m_util.is_concat(rhs) && is_concat_split_target(lhs))) + if (is_concat_target(lhs, rhs)) { return mk_eq_concat(lhs, rhs, result); - + } + if (swapped) { result = m().mk_eq(lhs, rhs); return BR_DONE; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 1c9b44b52..78d3fb4f1 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -137,6 +137,10 @@ class bv_rewriter : public poly_rewriter { bool is_concat_split_target(expr * t) const; br_status mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result); + bool is_add_mul_const(expr* e) const; + bool isolate_term(expr* lhs, expr* rhs, expr_ref & result); + bool has_numeral(app* e) const; + bool is_concat_target(expr* lhs, expr* rhs) const; void updt_local_params(params_ref const & p); diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 51b2c1b59..cef8d5fc7 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -424,6 +424,39 @@ namespace smt { }; + void theory_bv::add_fixed_eq(theory_var v1, theory_var v2) { + ++m_stats.m_num_eq_dynamic; + if (v1 > v2) { + std::swap(v1, v2); + } + unsigned sz = get_bv_size(v1); + ast_manager& m = get_manager(); + context & ctx = get_context(); + app* o1 = get_enode(v1)->get_owner(); + app* o2 = get_enode(v2)->get_owner(); + literal oeq = mk_eq(o1, o2, true); + TRACE("bv", + tout << mk_pp(o1, m) << " = " << mk_pp(o2, m) << " " + << ctx.get_scope_level() << "\n";); + literal_vector eqs; + for (unsigned i = 0; i < sz; ++i) { + literal l1 = m_bits[v1][i]; + literal l2 = m_bits[v2][i]; + expr_ref e1(m), e2(m); + e1 = mk_bit2bool(o1, i); + e2 = mk_bit2bool(o2, i); + literal eq = mk_eq(e1, e2, true); + ctx.mk_th_axiom(get_id(), l1, ~l2, ~eq); + ctx.mk_th_axiom(get_id(), ~l1, l2, ~eq); + ctx.mk_th_axiom(get_id(), l1, l2, eq); + ctx.mk_th_axiom(get_id(), ~l1, ~l2, eq); + ctx.mk_th_axiom(get_id(), eq, ~oeq); + eqs.push_back(~eq); + } + eqs.push_back(oeq); + ctx.mk_th_axiom(get_id(), eqs.size(), eqs.c_ptr()); + } + void theory_bv::fixed_var_eh(theory_var v) { numeral val; bool r = get_fixed_value(v, val); @@ -443,7 +476,9 @@ namespace smt { display_var(tout, v); display_var(tout, v2);); m_stats.m_num_th2core_eq++; - ctx.assign_eq(get_enode(v), get_enode(v2), eq_justification(js)); + add_fixed_eq(v, v2); + ctx.assign_eq(get_enode(v), get_enode(v2), eq_justification(js)); + m_fixed_var_table.insert(key, v2); } } else { @@ -1177,6 +1212,7 @@ namespace smt { } void theory_bv::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) { + m_stats.m_num_bit2core++; context & ctx = get_context(); SASSERT(ctx.get_assignment(antecedent) == l_true); @@ -1192,6 +1228,12 @@ namespace smt { } else { ctx.assign(consequent, mk_bit_eq_justification(v1, v2, consequent, antecedent)); + literal_vector lits; + lits.push_back(~consequent); + lits.push_back(antecedent); + lits.push_back(~mk_eq(get_enode(v1)->get_owner(), get_enode(v2)->get_owner(), false)); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + if (m_wpos[v2] == idx) find_wpos(v2); // REMARK: bit_eq_justification is marked as a theory_bv justification. @@ -1601,6 +1643,7 @@ namespace smt { st.update("bv dynamic diseqs", m_stats.m_num_diseq_dynamic); st.update("bv bit2core", m_stats.m_num_bit2core); st.update("bv->core eq", m_stats.m_num_th2core_eq); + st.update("bv dynamic eqs", m_stats.m_num_eq_dynamic); } #ifdef Z3DEBUG diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index bb0dcc907..17d03b412 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -34,6 +34,7 @@ namespace smt { struct theory_bv_stats { unsigned m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts; + unsigned m_num_eq_dynamic; void reset() { memset(this, 0, sizeof(theory_bv_stats)); } theory_bv_stats() { reset(); } }; @@ -124,8 +125,9 @@ namespace smt { typedef std::pair value_sort_pair; typedef pair_hash, unsigned_hash> value_sort_pair_hash; typedef map > value2var; - value2var m_fixed_var_table; + value2var m_fixed_var_table; + literal_vector m_tmp_literals; svector m_prop_queue; bool m_approximates_large_bvs; @@ -166,6 +168,7 @@ namespace smt { void find_wpos(theory_var v); friend class fixed_eq_justification; void fixed_var_eh(theory_var v); + void add_fixed_eq(theory_var v1, theory_var v2); bool get_fixed_value(theory_var v, numeral & result) const; void internalize_num(app * n); void internalize_add(app * n); diff --git a/src/tactic/bv/bit_blaster_tactic.h b/src/tactic/bv/bit_blaster_tactic.h index cb7f6f7a9..d840154b9 100644 --- a/src/tactic/bv/bit_blaster_tactic.h +++ b/src/tactic/bv/bit_blaster_tactic.h @@ -1,32 +1,33 @@ -/*++ + /*++ Copyright (c) 2011 Microsoft Corporation - -Module Name: - + + Module Name: + bit_blaster_tactic.h - -Abstract: - + + Abstract: + Apply bit-blasting to a given goal. - -Author: - + + Author: + Leonardo (leonardo) 2011-10-25 - -Notes: - ---*/ + + Notes: + + --*/ #ifndef BIT_BLASTER_TACTIC_H_ #define BIT_BLASTER_TACTIC_H_ - -#include"params.h" -#include"bit_blaster_rewriter.h" -class ast_manager; -class tactic; - + + #include"params.h" + #include"bit_blaster_rewriter.h" + class ast_manager; + class tactic; + tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p = params_ref()); tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p = params_ref()); -/* + /* ADD_TACTIC("bit-blast", "reduce bit-vector expressions into SAT.", "mk_bit_blaster_tactic(m, p)") -*/ -#endif + */ + #endif + diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index f1ffe4b53..e436d19c4 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -22,7 +22,7 @@ Revision History: #include"occurs.h" #include"cooperate.h" #include"goal_shared_occs.h" -#include"ast_smt2_pp.h" +#include"ast_pp.h" class solve_eqs_tactic : public tactic { struct imp { @@ -92,21 +92,23 @@ class solve_eqs_tactic : public tactic { } // Use: (= x def) and (= def x) - bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { + + bool trivial_solve1(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { + if (is_uninterp_const(lhs) && !m_candidate_vars.is_marked(lhs) && !occurs(lhs, rhs) && check_occs(lhs)) { var = to_app(lhs); def = rhs; pr = 0; return true; } - else if (is_uninterp_const(rhs) && !m_candidate_vars.is_marked(rhs) && !occurs(rhs, lhs) && check_occs(rhs)) { - var = to_app(rhs); - def = lhs; - if (m_produce_proofs) - pr = m().mk_commutativity(m().mk_eq(lhs, rhs)); - return true; + else { + return false; } - return false; + } + bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { + return + trivial_solve1(lhs, rhs, var, def, pr) || + trivial_solve1(rhs, lhs, var, def, pr); } // (ite c (= x t1) (= x t2)) --> (= x (ite c t1 t2)) From 65da0f9f3a8f901fd122670d725c6b9bca07b32c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 21 Dec 2015 06:07:50 -0800 Subject: [PATCH 03/35] updated seq Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 19 ++++++++----------- src/util/scoped_vector.h | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 753ddabd2..db96f3d0c 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -318,7 +318,8 @@ bool theory_seq::check_length_coherence_tbd() { // each variable that canonizes to itself can have length 0. unsigned sz = get_num_vars(); for (unsigned i = 0; i < sz; ++i) { - enode* n = get_enode(i); + unsigned j = (i + m_branch_variable_head) % sz; + enode* n = get_enode(j); expr* e = n->get_owner(); if (m_util.is_re(e)) { continue; @@ -332,15 +333,14 @@ bool theory_seq::check_length_coherence_tbd() { TRACE("seq", tout << "Unsolved " << mk_pp(e, m) << "\n";); #if 0 if (!assume_equality(e, emp)) { - // e = emp \/ e = head*tail & head = unit(v) + // e = emp \/ e = unit(head.elem(e))*tail(e) sort* char_sort = 0; VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); expr_ref tail(mk_skolem(symbol("seq.tail"), e), m); expr_ref v(mk_skolem(symbol("seq.head.elem"), e, 0, 0, char_sort), m); expr_ref head(m_util.str.mk_unit(v), m); expr_ref conc(m_util.str.mk_concat(head, tail), m); - literal e_eq_emp(mk_eq(e, emp, false)); - add_axiom(e_eq_emp, mk_eq(e, conc, false)); + add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); } #endif coherent = false; @@ -565,10 +565,9 @@ bool theory_seq::solve_nqs() { bool change = false; context & ctx = get_context(); for (unsigned i = 0; !ctx.inconsistent() && i < m_nqs.size(); ++i) { - change = solve_ne(i) || change; - if (m_nqs[i].is_solved()) { - m_nqs.erase_and_swap(i); - --i; + TRACE("seq", tout << i << " " << m_nqs.size() << "\n";); + if (!m_nqs[i].is_solved()) { + change = solve_ne(i) || change; } } return change; @@ -614,7 +613,7 @@ bool theory_seq::solve_ne(unsigned idx) { // continue } else { - TRACE("seq", tout << lhs.size() << "\n"; + TRACE("seq", for (unsigned j = 0; j < lhs.size(); ++j) { tout << mk_pp(lhs[j].get(), m) << " "; } @@ -625,11 +624,9 @@ bool theory_seq::solve_ne(unsigned idx) { expr_ref nl(lhs[j].get(), m); expr_ref nr(rhs[j].get(), m); if (m_util.is_seq(nl) || m_util.is_re(nl)) { - //std::cout << "push_ne " << nl << " != " << nr << "\n"; m_trail_stack.push(push_ne(*this, idx, nl, nr)); } else { - //std::cout << "push_lit\n"; literal lit(mk_eq(nl, nr, false)); m_trail_stack.push(push_lit(*this, idx, ~lit)); ctx.mark_as_relevant(lit); diff --git a/src/util/scoped_vector.h b/src/util/scoped_vector.h index a05b19487..bacfbac89 100644 --- a/src/util/scoped_vector.h +++ b/src/util/scoped_vector.h @@ -110,7 +110,7 @@ public: void erase_and_swap(unsigned i) { if (i + 1 < size()) { - set(i, m_elems[m_index[i]]); + set(i, m_elems[m_index[size()-1]]); } pop_back(); } From 9c6271ddeddab61d9d995926abb2e42717a5cd45 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 22 Dec 2015 10:43:18 -0800 Subject: [PATCH 04/35] add debugging facilities for github issues #384 #367 Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 2 +- src/api/api_ast.cpp | 2 + src/api/api_ast_vector.cpp | 1 + src/api/java/Context.java | 1 + src/api/ml/z3.ml | 912 ++++++++++++------------- src/api/z3_replayer.cpp | 10 +- src/api/z3_replayer.h | 2 +- src/ast/ast.cpp | 1 + src/ast/rewriter/expr_safe_replace.cpp | 16 +- src/ast/seq_decl_plugin.cpp | 23 +- src/ast/seq_decl_plugin.h | 9 +- src/smt/theory_seq.cpp | 6 +- src/smt/theory_seq_empty.h | 7 + src/util/mpf.cpp | 16 +- 14 files changed, 529 insertions(+), 479 deletions(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index febbf3eb2..fc0f1c939 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -1079,7 +1079,7 @@ def def_API(name, result, params): def mk_bindings(): exe_c.write("void register_z3_replayer_cmds(z3_replayer & in) {\n") for key, val in API2Id.items(): - exe_c.write(" in.register_cmd(%s, exec_%s);\n" % (key, val)) + exe_c.write(" in.register_cmd(%s, exec_%s, \"%s\");\n" % (key, val, val)) exe_c.write("}\n") def ml_method_name(name): diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 9f0ddbaa8..944e447c6 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -776,6 +776,8 @@ extern "C" { SET_ERROR_CODE(Z3_SORT_ERROR); RETURN_Z3(of_expr(0)); } + SASSERT(from[i]->get_ref_count() > 0); + SASSERT(to[i]->get_ref_count() > 0); } expr_safe_replace subst(m); for (unsigned i = 0; i < num_exprs; i++) { diff --git a/src/api/api_ast_vector.cpp b/src/api/api_ast_vector.cpp index e1d4d78ff..d6ff1bbb3 100644 --- a/src/api/api_ast_vector.cpp +++ b/src/api/api_ast_vector.cpp @@ -26,6 +26,7 @@ Revision History: extern "C" { Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c) { + std::cout << "ast-vector\n"; Z3_TRY; LOG_Z3_mk_ast_vector(c); RESET_ERROR_CODE(); diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 4d8484ced..8d2bc5b50 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -3822,6 +3822,7 @@ public class Context extends IDisposable m_Params_DRQ.clear(this); m_Probe_DRQ.clear(this); m_Solver_DRQ.clear(this); + m_Optimize_DRQ.clear(this); m_Statistics_DRQ.clear(this); m_Tactic_DRQ.clear(this); m_Fixedpoint_DRQ.clear(this); diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 84c70001e..8fdc795fb 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -60,7 +60,7 @@ struct o.inc_ref (context_gno ctx) no ; ( if not (is_null o.m_n_obj) then - o.dec_ref (context_gno ctx) o.m_n_obj ; + o.dec_ref (context_gno ctx) o.m_n_obj ; (context_sub1 ctx) ) ; o.m_n_obj <- no @@ -68,8 +68,8 @@ struct let z3obj_dispose o = if not (is_null o.m_n_obj) then ( - o.dec_ref (z3obj_gnc o) o.m_n_obj ; - (context_sub1 (z3obj_gc o)) + o.dec_ref (z3obj_gnc o) o.m_n_obj ; + (context_sub1 (z3obj_gc o)) ) ; o.m_n_obj <- null @@ -81,12 +81,12 @@ struct let z3_native_object_of_ast_ptr : context -> Z3native.ptr -> z3_native_object = fun ctx no -> let res : z3_native_object = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.inc_ref ; - dec_ref = Z3native.dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.inc_ref ; + dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; - res + res end open Internal @@ -137,18 +137,18 @@ struct let create_i ( ctx : context ) ( no : Z3native.ptr ) = let res : symbol = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = z3obj_nil_ref ; - dec_ref = z3obj_nil_ref } in + m_n_obj = null ; + inc_ref = z3obj_nil_ref ; + dec_ref = z3obj_nil_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res let create_s ( ctx : context ) ( no : Z3native.ptr ) = let res : symbol = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = z3obj_nil_ref ; - dec_ref = z3obj_nil_ref } in + m_n_obj = null ; + inc_ref = z3obj_nil_ref ; + dec_ref = z3obj_nil_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -156,7 +156,7 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = match (symbol_kind_of_int (Z3native.get_symbol_kind (context_gno ctx) no)) with | INT_SYMBOL -> (create_i ctx no) - | STRING_SYMBOL -> (create_s ctx no) + | STRING_SYMBOL -> (create_s ctx no) let gc ( x : symbol ) = (z3obj_gc x) let gnc ( x : symbol ) = (z3obj_gnc x) @@ -264,16 +264,16 @@ end = struct module ASTVector = struct type ast_vector = z3_native_object - + let create ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_vector = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.ast_vector_inc_ref ; - dec_ref = Z3native.ast_vector_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.ast_vector_inc_ref ; + dec_ref = Z3native.ast_vector_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res - + let mk_ast_vector ( ctx : context ) = (create ctx (Z3native.mk_ast_vector (context_gno ctx))) let get_size ( x : ast_vector ) = @@ -287,69 +287,69 @@ end = struct let resize ( x : ast_vector ) ( new_size : int ) = Z3native.ast_vector_resize (z3obj_gnc x) (z3obj_gno x) new_size - + let push ( x : ast_vector ) ( a : ast ) = Z3native.ast_vector_push (z3obj_gnc x) (z3obj_gno x) (z3obj_gno a) - + let translate ( x : ast_vector ) ( to_ctx : context ) = create to_ctx (Z3native.ast_vector_translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) let to_list ( x : ast_vector ) = - let xs = (get_size x) in + let xs = (get_size x) in let f i = (get x i) in mk_list f xs let to_expr_list ( x : ast_vector ) = - let xs = (get_size x) in + let xs = (get_size x) in let f i = (Expr.expr_of_ptr (z3obj_gc x) (z3obj_gno (get x i))) in mk_list f xs - + let to_string ( x : ast_vector ) = Z3native.ast_vector_to_string (z3obj_gnc x) (z3obj_gno x) end module ASTMap = - struct + struct type ast_map = z3_native_object - + let create ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_map = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.ast_map_inc_ref ; - dec_ref = Z3native.ast_map_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.ast_map_inc_ref ; + dec_ref = Z3native.ast_map_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; - res - + res + let mk_ast_map ( ctx : context ) = (create ctx (Z3native.mk_ast_map (context_gno ctx))) let astmap_of_ptr ( ctx : context ) ( no : Z3native.ptr ) = let res : ast_map = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.ast_map_inc_ref ; - dec_ref = Z3native.ast_map_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.ast_map_inc_ref ; + dec_ref = Z3native.ast_map_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res - + let contains ( x : ast_map ) ( key : ast ) = Z3native.ast_map_contains (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key) - + let find ( x : ast_map ) ( key : ast ) = ast_of_ptr (z3obj_gc x) (Z3native.ast_map_find (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key)) - + let insert ( x : ast_map ) ( key : ast ) ( value : ast ) = Z3native.ast_map_insert (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key) (z3obj_gno value) let erase ( x : ast_map ) ( key : ast ) = Z3native.ast_map_erase (z3obj_gnc x) (z3obj_gno x) (z3obj_gno key) - + let reset ( x : ast_map ) = Z3native.ast_map_reset (z3obj_gnc x) (z3obj_gno x) let get_size ( x : ast_map ) = Z3native.ast_map_size (z3obj_gnc x) (z3obj_gno x) - + let get_keys ( x : ast_map ) = let av = ASTVector.create (z3obj_gc x) (Z3native.ast_map_keys (z3obj_gnc x) (z3obj_gno x)) in (ASTVector.to_list av) @@ -369,7 +369,7 @@ end = struct | QUANTIFIER_AST | VAR_AST -> true | _ -> false - + let is_app ( x : ast ) = (get_ast_kind x) == APP_AST let is_var ( x : ast ) = (get_ast_kind x) == VAR_AST let is_quantifier ( x : ast ) = (get_ast_kind x) == QUANTIFIER_AST @@ -385,12 +385,12 @@ end = struct false else Z3native.is_eq_ast (z3obj_gnc a) (z3obj_gno a) (z3obj_gno b) - + let compare a b = if (get_id a) < (get_id b) then -1 else if (get_id a) > (get_id b) then 1 else - 0 - + 0 + let translate ( x : ast ) ( to_ctx : context ) = if (z3obj_gnc x) == (context_gno to_ctx) then x @@ -456,11 +456,11 @@ end = struct let equal : sort -> sort -> bool = fun a b -> (a == b) || if (gnc a) != (gnc b) then - false + false else - (Z3native.is_eq_sort (gnc a) (gno a) (gno b)) + (Z3native.is_eq_sort (gnc a) (gno a) (gno b)) - + let get_id ( x : sort ) = Z3native.get_sort_id (gnc x) (gno x) let get_sort_kind ( x : sort ) = (sort_kind_of_int (Z3native.get_sort_kind (gnc x) (gno x))) let get_name ( x : sort ) = (Symbol.create (gc x) (Z3native.get_sort_name (gnc x) (gno x))) @@ -468,9 +468,9 @@ end = struct let mk_uninterpreted ( ctx : context ) ( s : Symbol.symbol ) = let res = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.inc_ref ; - dec_ref = Z3native.dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.inc_ref ; + dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx (Z3native.mk_uninterpreted_sort (context_gno ctx) (Symbol.gno s))) ; (z3obj_create res) ; Sort(res) @@ -490,14 +490,14 @@ sig module Parameter : sig type parameter = - P_Int of int + P_Int of int | P_Dbl of float | P_Sym of Symbol.symbol | P_Srt of Sort.sort | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string - + val get_kind : parameter -> Z3enums.parameter_kind val get_int : parameter -> int val get_float : parameter -> float @@ -538,18 +538,18 @@ end = struct let create_ndr ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort list ) ( range : Sort.sort ) = let res = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.inc_ref ; - dec_ref = Z3native.dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.inc_ref ; + dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx (Z3native.mk_func_decl (context_gno ctx) (Symbol.gno name) (List.length domain) (Sort.sort_lton domain) (Sort.gno range))) ; (z3obj_create res) ; FuncDecl(res) let create_pdr ( ctx : context) ( prefix : string ) ( domain : Sort.sort list ) ( range : Sort.sort ) = let res = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.inc_ref ; - dec_ref = Z3native.dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.inc_ref ; + dec_ref = Z3native.dec_ref } in (z3obj_sno res ctx (Z3native.mk_fresh_func_decl (context_gno ctx) prefix (List.length domain) (Sort.sort_lton domain) (Sort.gno range))) ; (z3obj_create res) ; FuncDecl(res) @@ -568,51 +568,51 @@ end = struct | P_Ast of AST.ast | P_Fdl of func_decl | P_Rat of string - + let get_kind ( x : parameter ) = (match x with - | P_Int(_) -> PARAMETER_INT - | P_Dbl(_) -> PARAMETER_DOUBLE - | P_Sym(_) -> PARAMETER_SYMBOL - | P_Srt(_) -> PARAMETER_SORT - | P_Ast(_) -> PARAMETER_AST - | P_Fdl(_) -> PARAMETER_FUNC_DECL - | P_Rat(_) -> PARAMETER_RATIONAL) - + | P_Int(_) -> PARAMETER_INT + | P_Dbl(_) -> PARAMETER_DOUBLE + | P_Sym(_) -> PARAMETER_SYMBOL + | P_Srt(_) -> PARAMETER_SORT + | P_Ast(_) -> PARAMETER_AST + | P_Fdl(_) -> PARAMETER_FUNC_DECL + | P_Rat(_) -> PARAMETER_RATIONAL) + let get_int ( x : parameter ) = match x with - | P_Int(x) -> x - | _ -> raise (Z3native.Exception "parameter is not an int") - + | P_Int(x) -> x + | _ -> raise (Z3native.Exception "parameter is not an int") + let get_float ( x : parameter ) = match x with - | P_Dbl(x) -> x - | _ -> raise (Z3native.Exception "parameter is not a float") + | P_Dbl(x) -> x + | _ -> raise (Z3native.Exception "parameter is not a float") let get_symbol ( x : parameter ) = match x with - | P_Sym(x) -> x - | _ -> raise (Z3native.Exception "parameter is not a symbol") - + | P_Sym(x) -> x + | _ -> raise (Z3native.Exception "parameter is not a symbol") + let get_sort ( x : parameter ) = match x with - | P_Srt(x) -> x - | _ -> raise (Z3native.Exception "parameter is not a sort") + | P_Srt(x) -> x + | _ -> raise (Z3native.Exception "parameter is not a sort") let get_ast ( x : parameter ) = match x with - | P_Ast(x) -> x - | _ -> raise (Z3native.Exception "parameter is not an ast") + | P_Ast(x) -> x + | _ -> raise (Z3native.Exception "parameter is not an ast") let get_func_decl ( x : parameter ) = match x with - | P_Fdl(x) -> x - | _ -> raise (Z3native.Exception "parameter is not a func_decl") + | P_Fdl(x) -> x + | _ -> raise (Z3native.Exception "parameter is not a func_decl") let get_rational ( x : parameter ) = match x with - | P_Rat(x) -> x - | _ -> raise (Z3native.Exception "parameter is not a rational string") + | P_Rat(x) -> x + | _ -> raise (Z3native.Exception "parameter is not a rational string") end let mk_func_decl ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort list ) ( range : Sort.sort ) = @@ -710,19 +710,19 @@ end = struct let param_descrs_of_ptr ( ctx : context ) ( no : Z3native.ptr ) = let res : param_descrs = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.param_descrs_inc_ref ; - dec_ref = Z3native.param_descrs_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.param_descrs_inc_ref ; + dec_ref = Z3native.param_descrs_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res - + let validate ( x : param_descrs ) ( p : params ) = Z3native.params_validate (z3obj_gnc x) (z3obj_gno p) (z3obj_gno x) - + let get_kind ( x : param_descrs ) ( name : Symbol.symbol ) = (param_kind_of_int (Z3native.param_descrs_get_kind (z3obj_gnc x) (z3obj_gno x) (Symbol.gno name))) - + let get_names ( x : param_descrs ) = let n = Z3native.param_descrs_size (z3obj_gnc x) (z3obj_gno x) in let f i = Symbol.create (z3obj_gc x) (Z3native.param_descrs_get_name (z3obj_gnc x) (z3obj_gno x) i) in @@ -746,9 +746,9 @@ end = struct let mk_params ( ctx : context ) = let res : params = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.params_inc_ref ; - dec_ref = Z3native.params_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.params_inc_ref ; + dec_ref = Z3native.params_dec_ref } in (z3obj_sno res ctx (Z3native.mk_params (context_gno ctx))) ; (z3obj_create res) ; res @@ -807,32 +807,30 @@ end = struct let gno e = match e with Expr(a) -> (z3obj_gno a) let expr_of_ptr : context -> Z3native.ptr -> expr = fun ctx no -> + let e = z3_native_object_of_ast_ptr ctx no in if ast_kind_of_int (Z3native.get_ast_kind (context_gno ctx) no) == QUANTIFIER_AST then - Expr(z3_native_object_of_ast_ptr ctx no) + Expr(e) else let s = Z3native.get_sort (context_gno ctx) no in let sk = (sort_kind_of_int (Z3native.get_sort_kind (context_gno ctx) s)) in if (Z3native.is_algebraic_number (context_gno ctx) no) then - Expr(z3_native_object_of_ast_ptr ctx no) - else - if (Z3native.is_numeral_ast (context_gno ctx) no) then - match sk with - | REAL_SORT - | BOOL_SORT - | ARRAY_SORT - | BV_SORT - | ROUNDING_MODE_SORT - | RELATION_SORT - | UNINTERPRETED_SORT - | FLOATING_POINT_SORT - | INT_SORT - | DATATYPE_SORT - | FINITE_DOMAIN_SORT -> - Expr(z3_native_object_of_ast_ptr ctx no) - | _ -> - raise (Z3native.Exception "Unsupported numeral object") - else - Expr(z3_native_object_of_ast_ptr ctx no) + Expr(e) + else if (Z3native.is_numeral_ast (context_gno ctx) no) then + match sk with + | REAL_SORT + | BOOL_SORT + | ARRAY_SORT + | BV_SORT + | ROUNDING_MODE_SORT + | RELATION_SORT + | UNINTERPRETED_SORT + | FLOATING_POINT_SORT + | INT_SORT + | DATATYPE_SORT + | FINITE_DOMAIN_SORT -> Expr(e) + | _ -> raise (Z3native.Exception "Unsupported numeral object") + else + Expr(e) let expr_of_ast a = let q = (Z3enums.ast_kind_of_int (Z3native.get_ast_kind (z3obj_gnc a) (z3obj_gno a))) in @@ -866,9 +864,9 @@ end = struct let get_num_args ( x : expr ) = Z3native.get_app_num_args (gnc x) (gno x) let get_args ( x : expr ) = let n = (get_num_args x) in - let f i = expr_of_ptr (Expr.gc x) (Z3native.get_app_arg (gnc x) (gno x) i) in - mk_list f n - + let f i = expr_of_ptr (Expr.gc x) (Z3native.get_app_arg (gnc x) (gno x) i) in + mk_list f n + let update ( x : expr ) ( args : expr list ) = if ((AST.is_app (ast_of_expr x)) && (List.length args <> (get_num_args x))) then raise (Z3native.Exception "Number of arguments does not match") @@ -880,7 +878,7 @@ end = struct raise (Z3native.Exception "Argument sizes do not match") else expr_of_ptr (Expr.gc x) (Z3native.substitute (gnc x) (gno x) (List.length from) (expr_lton from) (expr_lton to_)) - + let substitute_one ( x : expr ) from to_ = substitute ( x : expr ) [ from ] [ to_ ] @@ -1012,10 +1010,10 @@ struct match e with Expr.Expr(a) -> let q = (Z3enums.ast_kind_of_int (Z3native.get_ast_kind (z3obj_gnc a) (z3obj_gno a))) in if (q != Z3enums.QUANTIFIER_AST) then - raise (Z3native.Exception "Invalid coercion") + raise (Z3native.Exception "Invalid coercion") else - Quantifier(e) - + Quantifier(e) + let gc ( x : quantifier ) = match (x) with Quantifier(e) -> (Expr.gc e) let gnc ( x : quantifier ) = match (x) with Quantifier(e) -> (Expr.gnc e) let gno ( x : quantifier ) = match (x) with Quantifier(e) -> (Expr.gno e) @@ -1023,25 +1021,25 @@ struct module Pattern = struct type pattern = Pattern of AST.ast - + let ast_of_pattern e = match e with Pattern(x) -> x let pattern_of_ast a = (* CMW: Unchecked ok? *) Pattern(a) - + let gc ( x : pattern ) = match (x) with Pattern(a) -> (z3obj_gc a) let gnc ( x : pattern ) = match (x) with Pattern(a) -> (z3obj_gnc a) let gno ( x : pattern ) = match (x) with Pattern(a) -> (z3obj_gno a) let get_num_terms ( x : pattern ) = - Z3native.get_pattern_num_terms (gnc x) (gno x) + Z3native.get_pattern_num_terms (gnc x) (gno x) let get_terms ( x : pattern ) = let n = (get_num_terms x) in let f i = (expr_of_ptr (gc x) (Z3native.get_pattern (gnc x) (gno x) i)) in mk_list f n - + let to_string ( x : pattern ) = Z3native.pattern_to_string (gnc x) (gno x) end @@ -1101,76 +1099,76 @@ struct raise (Z3native.Exception "Number of sorts does not match number of names") else if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier (context_gno ctx) true - (match weight with | None -> 1 | Some(x) -> x) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length sorts) (Sort.sort_lton sorts) - (Symbol.symbol_lton names) - (Expr.gno body))) + (match weight with | None -> 1 | Some(x) -> x) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (List.length sorts) (Sort.sort_lton sorts) + (Symbol.symbol_lton names) + (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_ex (context_gno ctx) true - (match weight with | None -> 1 | Some(x) -> x) - (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) - (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length nopatterns) (expr_lton nopatterns) - (List.length sorts) (Sort.sort_lton sorts) - (Symbol.symbol_lton names) - (Expr.gno body))) - + (match weight with | None -> 1 | Some(x) -> x) + (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) + (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (List.length nopatterns) (expr_lton nopatterns) + (List.length sorts) (Sort.sort_lton sorts) + (Symbol.symbol_lton names) + (Expr.gno body))) + let mk_forall_const ( ctx : context ) ( bound_constants : expr list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const (context_gno ctx) true - (match weight with | None -> 1 | Some(x) -> x) - (List.length bound_constants) (expr_lton bound_constants) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (Expr.gno body))) + (match weight with | None -> 1 | Some(x) -> x) + (List.length bound_constants) (expr_lton bound_constants) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const_ex (context_gno ctx) true - (match weight with | None -> 1 | Some(x) -> x) - (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) - (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) - (List.length bound_constants) (expr_lton bound_constants) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length nopatterns) (expr_lton nopatterns) - (Expr.gno body))) + (match weight with | None -> 1 | Some(x) -> x) + (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) + (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) + (List.length bound_constants) (expr_lton bound_constants) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (List.length nopatterns) (expr_lton nopatterns) + (Expr.gno body))) let mk_exists ( ctx : context ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (List.length sorts) != (List.length names) then raise (Z3native.Exception "Number of sorts does not match number of names") else if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier (context_gno ctx) false - (match weight with | None -> 1 | Some(x) -> x) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length sorts) (Sort.sort_lton sorts) - (Symbol.symbol_lton names) - (Expr.gno body))) + (match weight with | None -> 1 | Some(x) -> x) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (List.length sorts) (Sort.sort_lton sorts) + (Symbol.symbol_lton names) + (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_ex (context_gno ctx) false - (match weight with | None -> 1 | Some(x) -> x) - (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) - (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length nopatterns) (expr_lton nopatterns) - (List.length sorts) (Sort.sort_lton sorts) - (Symbol.symbol_lton names) - (Expr.gno body))) - + (match weight with | None -> 1 | Some(x) -> x) + (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) + (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (List.length nopatterns) (expr_lton nopatterns) + (List.length sorts) (Sort.sort_lton sorts) + (Symbol.symbol_lton names) + (Expr.gno body))) + let mk_exists_const ( ctx : context ) ( bound_constants : expr list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if ((List.length nopatterns) == 0 && quantifier_id == None && skolem_id == None) then Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const (context_gno ctx) false - (match weight with | None -> 1 | Some(x) -> x) - (List.length bound_constants) (expr_lton bound_constants) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (Expr.gno body))) + (match weight with | None -> 1 | Some(x) -> x) + (List.length bound_constants) (expr_lton bound_constants) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (Expr.gno body))) else Quantifier(expr_of_ptr ctx (Z3native.mk_quantifier_const_ex (context_gno ctx) false - (match weight with | None -> 1 | Some(x) -> x) - (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) - (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) - (List.length bound_constants) (expr_lton bound_constants) - (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) - (List.length nopatterns) (expr_lton nopatterns) - (Expr.gno body))) + (match weight with | None -> 1 | Some(x) -> x) + (match quantifier_id with | None -> null | Some(x) -> (Symbol.gno x)) + (match skolem_id with | None -> null | Some(x) -> (Symbol.gno x)) + (List.length bound_constants) (expr_lton bound_constants) + (List.length patterns) (let f x = (AST.ptr_of_ast (Pattern.ast_of_pattern x)) in (Array.of_list (List.map f patterns))) + (List.length nopatterns) (expr_lton nopatterns) + (Expr.gno body))) let mk_quantifier ( ctx : context ) ( universal : bool ) ( sorts : Sort.sort list ) ( names : Symbol.symbol list ) ( body : expr ) ( weight : int option ) ( patterns : Pattern.pattern list ) ( nopatterns : expr list ) ( quantifier_id : Symbol.symbol option ) ( skolem_id : Symbol.symbol option ) = if (universal) then @@ -1209,7 +1207,7 @@ struct let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( domain : Sort.sort ) ( range : Sort.sort ) = (Expr.mk_const ctx name (mk_sort ctx domain range)) - let mk_const_s ( ctx : context ) ( name : string ) ( domain : Sort.sort ) ( range : Sort.sort ) = + let mk_const_s ( ctx : context ) ( name : string ) ( domain : Sort.sort ) ( range : Sort.sort ) = mk_const ctx (Symbol.mk_string ctx name) domain range let mk_select ( ctx : context ) ( a : expr ) ( i : expr ) = @@ -1229,7 +1227,7 @@ struct expr_of_ptr ctx (Z3native.mk_array_default (context_gno ctx) (Expr.gno arg)) let mk_array_ext ( ctx : context) ( arg1 : expr ) ( arg2 : expr ) = - expr_of_ptr ctx (Z3native.mk_array_ext (context_gno ctx) (Expr.gno arg1) (Expr.gno arg2)) + expr_of_ptr ctx (Z3native.mk_array_ext (context_gno ctx) (Expr.gno arg1) (Expr.gno arg2)) end @@ -1305,7 +1303,7 @@ struct let is_relation ( x : expr ) = let nc = (Expr.gnc x) in ((Z3native.is_app (Expr.gnc x) (Expr.gno x)) && - (sort_kind_of_int (Z3native.get_sort_kind nc (Z3native.get_sort nc (Expr.gno x))) == RELATION_SORT)) + (sort_kind_of_int (Z3native.get_sort_kind nc (Z3native.get_sort nc (Expr.gno x))) == RELATION_SORT)) let is_store ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_STORE) let is_empty ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_RA_EMPTY) @@ -1335,7 +1333,7 @@ struct module Constructor = struct type constructor = z3_native_object - + module FieldNumTable = Hashtbl.Make(struct type t = AST.ast let equal x y = AST.compare x y = 0 @@ -1347,28 +1345,28 @@ struct let create ( ctx : context ) ( name : Symbol.symbol ) ( recognizer : Symbol.symbol ) ( field_names : Symbol.symbol list ) ( sorts : Sort.sort option list ) ( sort_refs : int list ) = let n = (List.length field_names) in if n != (List.length sorts) then - raise (Z3native.Exception "Number of field names does not match number of sorts") + raise (Z3native.Exception "Number of field names does not match number of sorts") else - if n != (List.length sort_refs) then - raise (Z3native.Exception "Number of field names does not match number of sort refs") - else + if n != (List.length sort_refs) then + raise (Z3native.Exception "Number of field names does not match number of sort refs") + else let ptr = (Z3native.mk_constructor (context_gno ctx) (Symbol.gno name) - (Symbol.gno recognizer) - n - (Symbol.symbol_lton field_names) - (Sort.sort_option_lton sorts) - (Array.of_list sort_refs)) in - let no : constructor = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = z3obj_nil_ref ; - dec_ref = z3obj_nil_ref} in - (z3obj_sno no ctx ptr) ; - (z3obj_create no) ; - let f = fun o -> Z3native.del_constructor (z3obj_gnc o) (z3obj_gno o) in - Gc.finalise f no ; - FieldNumTable.add _field_nums no n ; - no - + (Symbol.gno recognizer) + n + (Symbol.symbol_lton field_names) + (Sort.sort_option_lton sorts) + (Array.of_list sort_refs)) in + let no : constructor = { m_ctx = ctx ; + m_n_obj = null ; + inc_ref = z3obj_nil_ref ; + dec_ref = z3obj_nil_ref} in + (z3obj_sno no ctx ptr) ; + (z3obj_create no) ; + let f = fun o -> Z3native.del_constructor (z3obj_gnc o) (z3obj_gno o) in + Gc.finalise f no ; + FieldNumTable.add _field_nums no n ; + no + let get_num_fields ( x : constructor ) = FieldNumTable.find _field_nums x let get_constructor_decl ( x : constructor ) = @@ -1377,13 +1375,13 @@ struct let get_tester_decl ( x : constructor ) = let (_, b, _) = (Z3native.query_constructor (z3obj_gnc x) (z3obj_gno x) (get_num_fields x)) in - func_decl_of_ptr (z3obj_gc x) b + func_decl_of_ptr (z3obj_gc x) b let get_accessor_decls ( x : constructor ) = let (_, _, c) = (Z3native.query_constructor (z3obj_gnc x) (z3obj_gno x) (get_num_fields x)) in let f i = func_decl_of_ptr (z3obj_gc x) (Array.get c i) in mk_list f (Array.length c) - + end module ConstructorList = @@ -1392,9 +1390,9 @@ struct let create ( ctx : context ) ( c : Constructor.constructor list ) = let res : constructor_list = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = z3obj_nil_ref ; - dec_ref = z3obj_nil_ref} in + m_n_obj = null ; + inc_ref = z3obj_nil_ref ; + dec_ref = z3obj_nil_ref} in let f x =(z3obj_gno x) in (z3obj_sno res ctx (Z3native.mk_constructor_list (context_gno ctx) (List.length c) (Array.of_list (List.map f c)))) ; (z3obj_create res) ; @@ -1429,8 +1427,8 @@ struct let mk_sorts_s ( ctx : context ) ( names : string list ) ( c : Constructor.constructor list list ) = mk_sorts ctx ( - let f e = (Symbol.mk_string ctx e) in - List.map f names + let f e = (Symbol.mk_string ctx e) in + List.map f names ) c @@ -1600,27 +1598,27 @@ struct let get_big_int ( x : expr ) = if (is_int_numeral x) then - let s = (Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x)) in - (Big_int.big_int_of_string s) + let s = (Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x)) in + (Big_int.big_int_of_string s) else raise (Z3native.Exception "Conversion failed.") - + let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) = Expr.mk_const ctx name (mk_sort ctx) - + let mk_const_s ( ctx : context ) ( name : string ) = mk_const ctx (Symbol.mk_string ctx name) - + let mk_mod ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_mod (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) - + let mk_rem ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_rem (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_numeral_s ( ctx : context ) ( v : string ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno (mk_sort ctx))) - + let mk_numeral_i ( ctx : context ) ( v : int ) = expr_of_ptr ctx (Z3native.mk_int (context_gno ctx) v (Sort.gno (mk_sort ctx))) @@ -1634,60 +1632,60 @@ struct module Real = struct let mk_sort ( ctx : context ) = - Sort.sort_of_ptr ctx (Z3native.mk_real_sort (context_gno ctx)) + Sort.sort_of_ptr ctx (Z3native.mk_real_sort (context_gno ctx)) let get_numerator ( x : expr ) = expr_of_ptr (Expr.gc x) (Z3native.get_numerator (Expr.gnc x) (Expr.gno x)) - + let get_denominator ( x : expr ) = expr_of_ptr (Expr.gc x) (Z3native.get_denominator (Expr.gnc x) (Expr.gno x)) - + let get_ratio ( x : expr ) = if (is_rat_numeral x) then - let s = (Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x)) in - (Ratio.ratio_of_string s) + let s = (Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x)) in + (Ratio.ratio_of_string s) else raise (Z3native.Exception "Conversion failed.") let to_decimal_string ( x : expr ) ( precision : int ) = Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision - + let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) = Expr.mk_const ctx name (mk_sort ctx) - + let mk_const_s ( ctx : context ) ( name : string ) = mk_const ctx (Symbol.mk_string ctx name) let mk_numeral_nd ( ctx : context ) ( num : int ) ( den : int ) = if (den == 0) then - raise (Z3native.Exception "Denominator is zero") + raise (Z3native.Exception "Denominator is zero") else - expr_of_ptr ctx (Z3native.mk_real (context_gno ctx) num den) - + expr_of_ptr ctx (Z3native.mk_real (context_gno ctx) num den) + let mk_numeral_s ( ctx : context ) ( v : string ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno (mk_sort ctx))) - + let mk_numeral_i ( ctx : context ) ( v : int ) = expr_of_ptr ctx (Z3native.mk_int (context_gno ctx) v (Sort.gno (mk_sort ctx))) - + let mk_is_integer ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_is_int (context_gno ctx) (Expr.gno t))) - + let mk_real2int ( ctx : context ) ( t : expr ) = (expr_of_ptr ctx (Z3native.mk_real2int (context_gno ctx) (Expr.gno t))) module AlgebraicNumber = struct let to_upper ( x : expr ) ( precision : int ) = - expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_upper (Expr.gnc x) (Expr.gno x) precision) - + expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_upper (Expr.gnc x) (Expr.gno x) precision) + let to_lower ( x : expr ) precision = - expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_lower (Expr.gnc x) (Expr.gno x) precision) - + expr_of_ptr (Expr.gc x) (Z3native.get_algebraic_number_lower (Expr.gnc x) (Expr.gno x) precision) + let to_decimal_string ( x : expr ) ( precision : int ) = - Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision - + Z3native.get_numeral_decimal_string (Expr.gnc x) (Expr.gno x) precision + let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) end end @@ -1843,9 +1841,9 @@ struct let mk_sge ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = (expr_of_ptr ctx (Z3native.mk_bvsge (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_ugt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - (expr_of_ptr ctx (Z3native.mk_bvugt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) + (expr_of_ptr ctx (Z3native.mk_bvugt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sgt ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - (expr_of_ptr ctx (Z3native.mk_bvsgt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) + (expr_of_ptr ctx (Z3native.mk_bvsgt (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_concat ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_concat (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_extract ( ctx : context ) ( high : int ) ( low : int ) ( t : expr ) = @@ -1857,7 +1855,7 @@ struct let mk_repeat ( ctx : context ) ( i : int ) ( t : expr ) = expr_of_ptr ctx (Z3native.mk_repeat (context_gno ctx) i (Expr.gno t)) let mk_shl ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - expr_of_ptr ctx (Z3native.mk_bvshl (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) + expr_of_ptr ctx (Z3native.mk_bvshl (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_lshr ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_bvlshr (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_ashr ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = @@ -1869,15 +1867,15 @@ struct let mk_ext_rotate_left ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = expr_of_ptr ctx (Z3native.mk_ext_rotate_left (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_ext_rotate_right ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - expr_of_ptr ctx (Z3native.mk_ext_rotate_right (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) + expr_of_ptr ctx (Z3native.mk_ext_rotate_right (context_gno ctx) (Expr.gno t1) (Expr.gno t2)) let mk_bv2int ( ctx : context ) ( t : expr ) ( signed : bool ) = expr_of_ptr ctx (Z3native.mk_bv2int (context_gno ctx) (Expr.gno t) signed) let mk_add_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( signed : bool) = (expr_of_ptr ctx (Z3native.mk_bvadd_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2) signed)) let mk_add_no_underflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - (expr_of_ptr ctx (Z3native.mk_bvadd_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) + (expr_of_ptr ctx (Z3native.mk_bvadd_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sub_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - (expr_of_ptr ctx (Z3native.mk_bvsub_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) + (expr_of_ptr ctx (Z3native.mk_bvsub_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_sub_no_underflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( signed : bool) = (expr_of_ptr ctx (Z3native.mk_bvsub_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2) signed)) let mk_sdiv_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = @@ -1887,7 +1885,7 @@ struct let mk_mul_no_overflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( signed : bool) = (expr_of_ptr ctx (Z3native.mk_bvmul_no_overflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2) signed)) let mk_mul_no_underflow ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - (expr_of_ptr ctx (Z3native.mk_bvmul_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) + (expr_of_ptr ctx (Z3native.mk_bvmul_no_underflow (context_gno ctx) (Expr.gno t1) (Expr.gno t2))) let mk_numeral ( ctx : context ) ( v : string ) ( size : int ) = expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno (mk_sort ctx size))) end @@ -1897,66 +1895,66 @@ module FloatingPoint = struct module RoundingMode = struct - let mk_sort ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_rounding_mode_sort (context_gno ctx))) - let is_fprm ( x : expr ) = - (Sort.get_sort_kind (Expr.get_sort(x))) == ROUNDING_MODE_SORT - let mk_round_nearest_ties_to_even ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_round_nearest_ties_to_even (context_gno ctx))) - let mk_rne ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_rne (context_gno ctx))) - let mk_round_nearest_ties_to_away ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_round_nearest_ties_to_away (context_gno ctx))) - let mk_rna ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_rna (context_gno ctx))) - let mk_round_toward_positive ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_positive (context_gno ctx))) - let mk_rtp ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_rtp (context_gno ctx))) - let mk_round_toward_negative ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_negative (context_gno ctx))) - let mk_rtn ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_rtn (context_gno ctx))) - let mk_round_toward_zero ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_zero (context_gno ctx))) - let mk_rtz ( ctx : context ) = - (expr_of_ptr ctx (Z3native.mk_fpa_rtz (context_gno ctx))) + let mk_sort ( ctx : context ) = + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_rounding_mode_sort (context_gno ctx))) + let is_fprm ( x : expr ) = + (Sort.get_sort_kind (Expr.get_sort(x))) == ROUNDING_MODE_SORT + let mk_round_nearest_ties_to_even ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_round_nearest_ties_to_even (context_gno ctx))) + let mk_rne ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_rne (context_gno ctx))) + let mk_round_nearest_ties_to_away ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_round_nearest_ties_to_away (context_gno ctx))) + let mk_rna ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_rna (context_gno ctx))) + let mk_round_toward_positive ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_positive (context_gno ctx))) + let mk_rtp ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_rtp (context_gno ctx))) + let mk_round_toward_negative ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_negative (context_gno ctx))) + let mk_rtn ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_rtn (context_gno ctx))) + let mk_round_toward_zero ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_round_toward_zero (context_gno ctx))) + let mk_rtz ( ctx : context ) = + (expr_of_ptr ctx (Z3native.mk_fpa_rtz (context_gno ctx))) end - + let mk_sort ( ctx : context ) ( ebits : int ) ( sbits : int ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort (context_gno ctx) ebits sbits)) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort (context_gno ctx) ebits sbits)) let mk_sort_half ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_half (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_half (context_gno ctx))) let mk_sort_16 ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_16 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_16 (context_gno ctx))) let mk_sort_single ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_single (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_single (context_gno ctx))) let mk_sort_32 ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_32 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_32 (context_gno ctx))) let mk_sort_double ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_double (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_double (context_gno ctx))) let mk_sort_64 ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_64 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_64 (context_gno ctx))) let mk_sort_quadruple ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_quadruple (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_quadruple (context_gno ctx))) let mk_sort_128 ( ctx : context ) = - (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_128 (context_gno ctx))) + (Sort.sort_of_ptr ctx (Z3native.mk_fpa_sort_128 (context_gno ctx))) let mk_nan ( ctx : context ) ( s : Sort.sort ) = - (expr_of_ptr ctx (Z3native.mk_fpa_nan (context_gno ctx) (Sort.gno s))) + (expr_of_ptr ctx (Z3native.mk_fpa_nan (context_gno ctx) (Sort.gno s))) let mk_inf ( ctx : context ) ( s : Sort.sort ) ( negative : bool ) = - (expr_of_ptr ctx (Z3native.mk_fpa_inf (context_gno ctx) (Sort.gno s) negative)) + (expr_of_ptr ctx (Z3native.mk_fpa_inf (context_gno ctx) (Sort.gno s) negative)) let mk_zero ( ctx : context ) ( s : Sort.sort ) ( negative : bool ) = - (expr_of_ptr ctx (Z3native.mk_fpa_zero (context_gno ctx) (Sort.gno s) negative)) + (expr_of_ptr ctx (Z3native.mk_fpa_zero (context_gno ctx) (Sort.gno s) negative)) let mk_fp ( ctx : context ) ( sign : expr ) ( exponent : expr ) ( significand : expr ) = - (expr_of_ptr ctx (Z3native.mk_fpa_fp (context_gno ctx) (Expr.gno sign) (Expr.gno exponent) (Expr.gno significand))) + (expr_of_ptr ctx (Z3native.mk_fpa_fp (context_gno ctx) (Expr.gno sign) (Expr.gno exponent) (Expr.gno significand))) let mk_numeral_f ( ctx : context ) ( value : float ) ( s : Sort.sort ) = - (expr_of_ptr ctx (Z3native.mk_fpa_numeral_double (context_gno ctx) value (Sort.gno s))) + (expr_of_ptr ctx (Z3native.mk_fpa_numeral_double (context_gno ctx) value (Sort.gno s))) let mk_numeral_i ( ctx : context ) ( value : int ) ( s : Sort.sort ) = - (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int (context_gno ctx) value (Sort.gno s))) + (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int (context_gno ctx) value (Sort.gno s))) let mk_numeral_i_u ( ctx : context ) ( sign : bool ) ( exponent : int ) ( significand : int ) ( s : Sort.sort ) = - (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int64_uint64 (context_gno ctx) sign exponent significand (Sort.gno s))) + (expr_of_ptr ctx (Z3native.mk_fpa_numeral_int64_uint64 (context_gno ctx) sign exponent significand (Sort.gno s))) let mk_numeral_s ( ctx : context ) ( v : string ) ( s : Sort.sort ) = (expr_of_ptr ctx (Z3native.mk_numeral (context_gno ctx) v (Sort.gno s))) @@ -1991,7 +1989,7 @@ struct let is_to_sbv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_SBV) let is_to_real ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_REAL) let is_to_ieee_bv ( x : expr ) = (AST.is_app (Expr.ast_of_expr x)) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) == OP_FPA_TO_IEEE_BV) - + let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) let mk_const ( ctx : context ) ( name : Symbol.symbol ) ( s : Sort.sort ) = Expr.mk_const ctx name s @@ -2064,24 +2062,24 @@ struct expr_of_ptr ctx (Z3native.mk_fpa_to_real (context_gno ctx) (Expr.gno t)) let get_ebits ( ctx : context ) ( s : Sort.sort ) = - (Z3native.fpa_get_ebits (context_gno ctx) (Sort.gno s)) + (Z3native.fpa_get_ebits (context_gno ctx) (Sort.gno s)) let get_sbits ( ctx : context ) ( s : Sort.sort ) = - (Z3native.fpa_get_sbits (context_gno ctx) (Sort.gno s)) + (Z3native.fpa_get_sbits (context_gno ctx) (Sort.gno s)) let get_numeral_sign ( ctx : context ) ( t : expr ) = - (Z3native.fpa_get_numeral_sign (context_gno ctx) (Expr.gno t)) + (Z3native.fpa_get_numeral_sign (context_gno ctx) (Expr.gno t)) let get_numeral_significand_string ( ctx : context ) ( t : expr ) = - (Z3native.fpa_get_numeral_significand_string (context_gno ctx) (Expr.gno t)) + (Z3native.fpa_get_numeral_significand_string (context_gno ctx) (Expr.gno t)) let get_numeral_significand_uint ( ctx : context ) ( t : expr ) = - (Z3native.fpa_get_numeral_significand_uint64 (context_gno ctx) (Expr.gno t)) + (Z3native.fpa_get_numeral_significand_uint64 (context_gno ctx) (Expr.gno t)) let get_numeral_exponent_string ( ctx : context ) ( t : expr ) = - (Z3native.fpa_get_numeral_exponent_string (context_gno ctx) (Expr.gno t)) + (Z3native.fpa_get_numeral_exponent_string (context_gno ctx) (Expr.gno t)) let get_numeral_exponent_int ( ctx : context ) ( t : expr ) = - (Z3native.fpa_get_numeral_exponent_int64 (context_gno ctx) (Expr.gno t)) + (Z3native.fpa_get_numeral_exponent_int64 (context_gno ctx) (Expr.gno t)) let mk_to_ieee_bv ( ctx : context ) ( t : expr ) = - (expr_of_ptr ctx (Z3native.mk_fpa_to_ieee_bv (context_gno ctx) (Expr.gno t))) + (expr_of_ptr ctx (Z3native.mk_fpa_to_ieee_bv (context_gno ctx) (Expr.gno t))) let mk_to_fp_int_real ( ctx : context ) ( rm : expr ) ( exponent : expr ) ( significand : expr ) ( s : Sort.sort ) = - (expr_of_ptr ctx (Z3native.mk_fpa_to_fp_int_real (context_gno ctx) (Expr.gno rm) (Expr.gno exponent) (Expr.gno significand) (Sort.gno s))) + (expr_of_ptr ctx (Z3native.mk_fpa_to_fp_int_real (context_gno ctx) (Expr.gno rm) (Expr.gno exponent) (Expr.gno significand) (Sort.gno s))) let numeral_to_string ( x : expr ) = Z3native.get_numeral_string (Expr.gnc x) (Expr.gno x) end @@ -2137,9 +2135,9 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : goal = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.goal_inc_ref ; - dec_ref = Z3native.goal_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.goal_inc_ref ; + dec_ref = Z3native.goal_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -2176,7 +2174,7 @@ struct let get_formulas ( x : goal ) = let n = get_size x in let f i = ((expr_of_ptr (z3obj_gc x) - (Z3native.goal_formula (z3obj_gnc x) (z3obj_gno x) i))) in + (Z3native.goal_formula (z3obj_gnc x) (z3obj_gno x) i))) in mk_list f n let get_num_exprs ( x : goal ) = Z3native.goal_num_exprs (z3obj_gnc x) (z3obj_gno x) @@ -2200,9 +2198,9 @@ struct Z3native.apply_result_inc_ref (z3obj_gnc x) arn ; let sg = Z3native.apply_result_get_num_subgoals (z3obj_gnc x) arn in let res = if sg == 0 then - raise (Z3native.Exception "No subgoals") + raise (Z3native.Exception "No subgoals") else - Z3native.apply_result_get_subgoal (z3obj_gnc x) arn 0 in + Z3native.apply_result_get_subgoal (z3obj_gnc x) arn 0 in Z3native.apply_result_dec_ref (z3obj_gnc x) arn ; Z3native.tactic_dec_ref (z3obj_gnc x) tn ; create (z3obj_gc x) res @@ -2213,13 +2211,13 @@ struct let to_string ( x : goal ) = Z3native.goal_to_string (z3obj_gnc x) (z3obj_gno x) let as_expr ( x : goal ) = - let n = get_size x in - if n = 0 then - (Boolean.mk_true (z3obj_gc x)) - else if n = 1 then - (List.hd (get_formulas x)) - else - (Boolean.mk_and (z3obj_gc x) (get_formulas x)) + let n = get_size x in + if n = 0 then + (Boolean.mk_true (z3obj_gc x)) + else if n = 1 then + (List.hd (get_formulas x)) + else + (Boolean.mk_and (z3obj_gc x) (get_formulas x)) end @@ -2229,9 +2227,9 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : model = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.model_inc_ref ; - dec_ref = Z3native.model_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.model_inc_ref ; + dec_ref = Z3native.model_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -2242,40 +2240,40 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : func_interp = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.func_interp_inc_ref ; - dec_ref = Z3native.func_interp_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.func_interp_inc_ref ; + dec_ref = Z3native.func_interp_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res - + module FuncEntry = - struct + struct type func_entry = z3_native_object - + let create ( ctx : context ) ( no : Z3native.ptr ) = - let res : func_entry = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.func_entry_inc_ref ; - dec_ref = Z3native.func_entry_dec_ref } in - (z3obj_sno res ctx no) ; - (z3obj_create res) ; - res - + let res : func_entry = { m_ctx = ctx ; + m_n_obj = null ; + inc_ref = Z3native.func_entry_inc_ref ; + dec_ref = Z3native.func_entry_dec_ref } in + (z3obj_sno res ctx no) ; + (z3obj_create res) ; + res + let get_value ( x : func_entry ) = - expr_of_ptr (z3obj_gc x) (Z3native.func_entry_get_value (z3obj_gnc x) (z3obj_gno x)) + expr_of_ptr (z3obj_gc x) (Z3native.func_entry_get_value (z3obj_gnc x) (z3obj_gno x)) let get_num_args ( x : func_entry ) = Z3native.func_entry_get_num_args (z3obj_gnc x) (z3obj_gno x) - + let get_args ( x : func_entry ) = - let n = (get_num_args x) in - let f i = (expr_of_ptr (z3obj_gc x) (Z3native.func_entry_get_arg (z3obj_gnc x) (z3obj_gno x) i)) in - mk_list f n - + let n = (get_num_args x) in + let f i = (expr_of_ptr (z3obj_gc x) (Z3native.func_entry_get_arg (z3obj_gnc x) (z3obj_gno x) i)) in + mk_list f n + let to_string ( x : func_entry ) = - let a = (get_args x) in - let f c p = (p ^ (Expr.to_string c) ^ ", ") in - "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") + let a = (get_args x) in + let f c p = (p ^ (Expr.to_string c) ^ ", ") in + "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") end let get_num_entries ( x: func_interp ) = Z3native.func_interp_get_num_entries (z3obj_gnc x) (z3obj_gno x) @@ -2291,14 +2289,14 @@ struct let to_string ( x : func_interp ) = let f c p = ( - let n = (FuncEntry.get_num_args c) in - p ^ - let g c p = (p ^ (Expr.to_string c) ^ ", ") in - (if n > 1 then "[" else "") ^ - (List.fold_right - g - (FuncEntry.get_args c) - ((if n > 1 then "]" else "") ^ " -> " ^ (Expr.to_string (FuncEntry.get_value c)) ^ ", ")) + let n = (FuncEntry.get_num_args c) in + p ^ + let g c p = (p ^ (Expr.to_string c) ^ ", ") in + (if n > 1 then "[" else "") ^ + (List.fold_right + g + (FuncEntry.get_args c) + ((if n > 1 then "]" else "") ^ " -> " ^ (Expr.to_string (FuncEntry.get_value c)) ^ ", ")) ) in List.fold_right f (get_entries x) ("else -> " ^ (Expr.to_string (get_else x)) ^ "]") end @@ -2310,9 +2308,9 @@ struct else let np = Z3native.model_get_const_interp (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f) in if (Z3native.is_null np) then - None + None else - Some (expr_of_ptr (z3obj_gc x) np) + Some (expr_of_ptr (z3obj_gc x) np) let get_const_interp_e ( x : model ) ( a : expr ) = get_const_interp x (Expr.get_func_decl a) @@ -2322,20 +2320,20 @@ struct if (FuncDecl.get_arity f) == 0 then let n = Z3native.model_get_const_interp (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f) in if (Z3native.is_null n) then - None + None else - match sk with - | ARRAY_SORT -> - if not (Z3native.is_as_array (z3obj_gnc x) n) then - raise (Z3native.Exception "Argument was not an array constant") - else - let fd = Z3native.get_as_array_func_decl (z3obj_gnc x) n in + match sk with + | ARRAY_SORT -> + if not (Z3native.is_as_array (z3obj_gnc x) n) then + raise (Z3native.Exception "Argument was not an array constant") + else + let fd = Z3native.get_as_array_func_decl (z3obj_gnc x) n in get_func_interp x (func_decl_of_ptr (z3obj_gc x) fd) - | _ -> raise (Z3native.Exception "Constant functions do not have a function interpretation; use ConstInterp"); + | _ -> raise (Z3native.Exception "Constant functions do not have a function interpretation; use ConstInterp"); else let n = (Z3native.model_get_func_interp (z3obj_gnc x) (z3obj_gno x) (FuncDecl.gno f)) in if (Z3native.is_null n) then None else Some (FuncInterp.create (z3obj_gc x) n) - + (** The number of constants that have an interpretation in the model. *) let get_num_consts ( x : model ) = Z3native.model_get_num_consts (z3obj_gnc x) (z3obj_gno x) @@ -2389,9 +2387,9 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : probe = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.probe_inc_ref ; - dec_ref = Z3native.probe_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.probe_inc_ref ; + dec_ref = Z3native.probe_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -2449,9 +2447,9 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : tactic = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.tactic_inc_ref ; - dec_ref = Z3native.tactic_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.tactic_inc_ref ; + dec_ref = Z3native.tactic_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -2459,30 +2457,30 @@ struct module ApplyResult = struct type apply_result = z3_native_object - + let create ( ctx : context ) ( no : Z3native.ptr ) = let res : apply_result = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.apply_result_inc_ref ; - dec_ref = Z3native.apply_result_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.apply_result_inc_ref ; + dec_ref = Z3native.apply_result_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res - + let get_num_subgoals ( x : apply_result ) = Z3native.apply_result_get_num_subgoals (z3obj_gnc x) (z3obj_gno x) - + let get_subgoals ( x : apply_result ) = let n = (get_num_subgoals x) in let f i = Goal.create (z3obj_gc x) (Z3native.apply_result_get_subgoal (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n - + let get_subgoal ( x : apply_result ) ( i : int ) = Goal.create (z3obj_gc x) (Z3native.apply_result_get_subgoal (z3obj_gnc x) (z3obj_gno x) i) - + let convert_model ( x : apply_result ) ( i : int ) ( m : Model.model ) = Model.create (z3obj_gc x) (Z3native.apply_result_convert_model (z3obj_gnc x) (z3obj_gno x) i (z3obj_gno m)) - + let to_string ( x : apply_result ) = Z3native.apply_result_to_string (z3obj_gnc x) (z3obj_gno x) end @@ -2515,10 +2513,10 @@ struct | Some(x) -> (Some (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno c) x))) in match (List.fold_left f None ts) with | None -> - create ctx (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t1) (z3obj_gno t2)) + create ctx (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t1) (z3obj_gno t2)) | Some(x) -> - let o = (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t2) x) in - create ctx (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t1) o) + let o = (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t2) x) in + create ctx (Z3native.tactic_and_then (context_gno ctx) (z3obj_gno t1) o) let or_else ( ctx : context ) ( t1 : tactic ) ( t2 : tactic ) = create ctx (Z3native.tactic_or_else (context_gno ctx) (z3obj_gno t1) (z3obj_gno t2)) @@ -2566,14 +2564,14 @@ end module Statistics = -struct +struct type statistics = z3_native_object let create ( ctx : context ) ( no : Z3native.ptr ) = let res : statistics = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.stats_inc_ref ; - dec_ref = Z3native.stats_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.stats_inc_ref ; + dec_ref = Z3native.stats_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -2582,44 +2580,44 @@ struct module Entry = struct type statistics_entry = { - mutable m_key : string; - mutable m_is_int : bool ; - mutable m_is_float : bool ; - mutable m_int : int ; - mutable m_float : float } - + mutable m_key : string; + mutable m_is_int : bool ; + mutable m_is_float : bool ; + mutable m_int : int ; + mutable m_float : float } + let create_si k v = - let res : statistics_entry = { - m_key = k ; - m_is_int = true ; - m_is_float = false ; - m_int = v ; - m_float = 0.0 - } in - res + let res : statistics_entry = { + m_key = k ; + m_is_int = true ; + m_is_float = false ; + m_int = v ; + m_float = 0.0 + } in + res let create_sd k v = - let res : statistics_entry = { - m_key = k ; - m_is_int = false ; - m_is_float = true ; - m_int = 0 ; - m_float = v - } in - res - + let res : statistics_entry = { + m_key = k ; + m_is_int = false ; + m_is_float = true ; + m_int = 0 ; + m_float = v + } in + res + let get_key (x : statistics_entry) = x.m_key - let get_int (x : statistics_entry) = x.m_int + let get_int (x : statistics_entry) = x.m_int let get_float (x : statistics_entry) = x.m_float let is_int (x : statistics_entry) = x.m_is_int let is_float (x : statistics_entry) = x.m_is_float let to_string_value (x : statistics_entry) = - if (is_int x) then - string_of_int (get_int x) - else if (is_float x) then - string_of_float (get_float x) - else + if (is_int x) then + string_of_int (get_int x) + else if (is_float x) then + string_of_float (get_float x) + else raise (Z3native.Exception "Unknown statistical entry type") let to_string ( x : statistics_entry ) = (get_key x) ^ ": " ^ (to_string_value x) end @@ -2631,11 +2629,11 @@ struct let get_entries ( x : statistics ) = let n = (get_size x ) in let f i = ( - let k = Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i in - if (Z3native.stats_is_uint (z3obj_gnc x) (z3obj_gno x) i) then - (Entry.create_si k (Z3native.stats_get_uint_value (z3obj_gnc x) (z3obj_gno x) i)) - else - (Entry.create_sd k (Z3native.stats_get_double_value (z3obj_gnc x) (z3obj_gno x) i)) + let k = Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i in + if (Z3native.stats_is_uint (z3obj_gnc x) (z3obj_gno x) i) then + (Entry.create_si k (Z3native.stats_get_uint_value (z3obj_gnc x) (z3obj_gno x) i)) + else + (Entry.create_sd k (Z3native.stats_get_double_value (z3obj_gnc x) (z3obj_gno x) i)) ) in mk_list f n @@ -2643,7 +2641,7 @@ struct let n = (get_size x) in let f i = (Z3native.stats_get_key (z3obj_gnc x) (z3obj_gno x) i) in mk_list f n - + let get ( x : statistics ) ( key : string ) = let f p c = (if ((Entry.get_key c) == key) then (Some c) else p) in List.fold_left f None (get_entries x) @@ -2657,9 +2655,9 @@ struct let create ( ctx : context ) ( no : Z3native.ptr ) = let res : solver = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.solver_inc_ref ; - dec_ref = Z3native.solver_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.solver_inc_ref ; + dec_ref = Z3native.solver_dec_ref } in (z3obj_sno res ctx no) ; (z3obj_create res) ; res @@ -2695,7 +2693,7 @@ struct else let f a b = (Z3native.solver_assert_and_track (z3obj_gnc x) (z3obj_gno x) (Expr.gno a) (Expr.gno b)) in ignore (List.iter2 f cs ps) - + let assert_and_track ( x : solver ) ( c : expr ) ( p : expr ) = Z3native.solver_assert_and_track (z3obj_gnc x) (z3obj_gno x) (Expr.gno c) (Expr.gno p) @@ -2710,30 +2708,30 @@ struct let check ( x : solver ) ( assumptions : expr list ) = let r = if ((List.length assumptions) == 0) then - lbool_of_int (Z3native.solver_check (z3obj_gnc x) (z3obj_gno x)) + lbool_of_int (Z3native.solver_check (z3obj_gnc x) (z3obj_gno x)) else - let f x = (Expr.gno x) in - lbool_of_int (Z3native.solver_check_assumptions (z3obj_gnc x) (z3obj_gno x) (List.length assumptions) (Array.of_list (List.map f assumptions))) + let f x = (Expr.gno x) in + lbool_of_int (Z3native.solver_check_assumptions (z3obj_gnc x) (z3obj_gno x) (List.length assumptions) (Array.of_list (List.map f assumptions))) in match r with | L_TRUE -> SATISFIABLE | L_FALSE -> UNSATISFIABLE | _ -> UNKNOWN - + let get_model ( x : solver ) = let q = Z3native.solver_get_model (z3obj_gnc x) (z3obj_gno x) in if (Z3native.is_null q) then None else Some (Model.create (z3obj_gc x) q) - + let get_proof ( x : solver ) = let q = Z3native.solver_get_proof (z3obj_gnc x) (z3obj_gno x) in if (Z3native.is_null q) then None else Some (expr_of_ptr (z3obj_gc x) q) - + let get_unsat_core ( x : solver ) = let av = AST.ASTVector.create (z3obj_gc x) (Z3native.solver_get_unsat_core (z3obj_gnc x) (z3obj_gno x)) in (AST.ASTVector.to_expr_list av) @@ -2758,7 +2756,7 @@ struct create ctx (Z3native.mk_solver_from_tactic (context_gno ctx) (z3obj_gno t)) let translate ( x : solver ) ( to_ctx : context ) = - create to_ctx (Z3native.solver_translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) + create to_ctx (Z3native.solver_translate (z3obj_gnc x) (z3obj_gno x) (context_gno to_ctx)) let to_string ( x : solver ) = Z3native.solver_to_string (z3obj_gnc x) (z3obj_gno x) end @@ -2770,9 +2768,9 @@ struct let create ( ctx : context ) = let res : fixedpoint = { m_ctx = ctx ; - m_n_obj = null ; - inc_ref = Z3native.fixedpoint_inc_ref ; - dec_ref = Z3native.fixedpoint_dec_ref } in + m_n_obj = null ; + inc_ref = Z3native.fixedpoint_inc_ref ; + dec_ref = Z3native.fixedpoint_dec_ref } in (z3obj_sno res ctx (Z3native.mk_fixedpoint (context_gno ctx))) ; (z3obj_create res) ; res @@ -2815,7 +2813,7 @@ struct | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE | _ -> Solver.UNKNOWN - + let push ( x : fixedpoint ) = Z3native.fixedpoint_push (z3obj_gnc x) (z3obj_gno x) @@ -2844,7 +2842,7 @@ struct None else Some (expr_of_ptr (z3obj_gc x) q) - + let add_cover ( x : fixedpoint ) ( level : int ) ( predicate : func_decl ) ( property : expr ) = Z3native.fixedpoint_add_cover (z3obj_gnc x) (z3obj_gno x) level (FuncDecl.gno predicate) (Expr.gno property) @@ -2980,13 +2978,13 @@ struct raise (Z3native.Exception "Argument size mismatch") else Z3native.parse_smtlib_string (context_gno ctx) str - cs - (Symbol.symbol_lton sort_names) - (Sort.sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) - + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) + let parse_smtlib_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in @@ -2996,12 +2994,12 @@ struct raise (Z3native.Exception "Argument size mismatch") else Z3native.parse_smtlib_file (context_gno ctx) file_name - cs - (Symbol.symbol_lton sort_names) - (Sort.sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))) let get_num_smtlib_formulas ( ctx : context ) = Z3native.get_smtlib_num_formulas (context_gno ctx) @@ -3040,13 +3038,13 @@ struct raise (Z3native.Exception "Argument size mismatch") else (expr_of_ptr ctx (Z3native.parse_smtlib2_string (context_gno ctx) str - cs - (Symbol.symbol_lton sort_names) - (Sort.sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) - + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) + let parse_smtlib2_file ( ctx : context ) ( file_name : string ) ( sort_names : Symbol.symbol list ) ( sorts : Sort.sort list ) ( decl_names : Symbol.symbol list ) ( decls : func_decl list ) = let csn = (List.length sort_names) in let cs = (List.length sorts) in @@ -3056,12 +3054,12 @@ struct raise (Z3native.Exception "Argument size mismatch") else (expr_of_ptr ctx (Z3native.parse_smtlib2_string (context_gno ctx) file_name - cs - (Symbol.symbol_lton sort_names) - (Sort.sort_lton sorts) - cd - (Symbol.symbol_lton decl_names) - (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) + cs + (Symbol.symbol_lton sort_names) + (Sort.sort_lton sorts) + cd + (Symbol.symbol_lton decl_names) + (let f x = FuncDecl.gno x in (Array.of_list (List.map f decls))))) end module Interpolation = @@ -3102,21 +3100,21 @@ struct match r with | 0 -> raise (Z3native.Exception "Interpolation problem could not be read.") | _ -> - let f1 i = (expr_of_ptr ctx (Array.get cnsts i)) in - let f2 i = (Array.get parents i) in - let f3 i = (expr_of_ptr ctx (Array.get theory i)) in - ((mk_list f1 num), - (mk_list f2 num), - (mk_list f3 num_theory)) + let f1 i = (expr_of_ptr ctx (Array.get cnsts i)) in + let f2 i = (Array.get parents i) in + let f3 i = (expr_of_ptr ctx (Array.get theory i)) in + ((mk_list f1 num), + (mk_list f2 num), + (mk_list f3 num_theory)) let check_interpolant ( ctx : context ) ( num : int ) ( cnsts : Expr.expr list ) ( parents : int list ) ( interps : Expr.expr list ) ( num_theory : int ) ( theory : Expr.expr list ) = let (r, str) = (Z3native.check_interpolant (context_gno ctx) - num - (let f x = Expr.gno x in (Array.of_list (List.map f cnsts))) - (Array.of_list parents) - (let f x = Expr.gno x in (Array.of_list (List.map f interps))) - num_theory - (let f x = Expr.gno x in (Array.of_list (List.map f theory)))) in + num + (let f x = Expr.gno x in (Array.of_list (List.map f cnsts))) + (Array.of_list parents) + (let f x = Expr.gno x in (Array.of_list (List.map f interps))) + num_theory + (let f x = Expr.gno x in (Array.of_list (List.map f theory)))) in match (lbool_of_int r) with | L_UNDEF -> raise (Z3native.Exception "Interpolant could not be verified.") | L_FALSE -> raise (Z3native.Exception "Interpolant could not be verified.") diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 927521b65..8ce81ec31 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -46,6 +46,7 @@ struct z3_replayer::imp { size_t m_ptr; size_t_map m_heap; svector m_cmds; + vector m_cmds_names; enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; @@ -509,6 +510,7 @@ struct z3_replayer::imp { if (idx >= m_cmds.size()) throw z3_replayer_exception("invalid command"); try { + TRACE("z3_replayer_cmd", tout << m_cmds_names[idx] << "\n";); m_cmds[idx](m_owner); } catch (z3_error & ex) { @@ -672,9 +674,11 @@ struct z3_replayer::imp { m_result = obj; } - void register_cmd(unsigned id, z3_replayer_cmd cmd) { + void register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name) { m_cmds.reserve(id+1, 0); + m_cmds_names.reserve(id+1, ""); m_cmds[id] = cmd; + m_cmds_names[id] = name; } void reset() { @@ -786,8 +790,8 @@ void z3_replayer::store_result(void * obj) { return m_imp->store_result(obj); } -void z3_replayer::register_cmd(unsigned id, z3_replayer_cmd cmd) { - return m_imp->register_cmd(id, cmd); +void z3_replayer::register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name) { + return m_imp->register_cmd(id, cmd, name); } void z3_replayer::parse() { diff --git a/src/api/z3_replayer.h b/src/api/z3_replayer.h index 6c566d553..88654363d 100644 --- a/src/api/z3_replayer.h +++ b/src/api/z3_replayer.h @@ -62,7 +62,7 @@ public: void ** get_obj_addr(unsigned pos); void store_result(void * obj); - void register_cmd(unsigned id, z3_replayer_cmd cmd); + void register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name); }; #endif diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 2ee6c7556..ab1a5a5af 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1656,6 +1656,7 @@ ast * ast_manager::register_node_core(ast * n) { n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk(); + TRACE("ast", tout << "Object " << n->m_id << " was created.\n";); TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";); // increment reference counters diff --git a/src/ast/rewriter/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp index b43577960..8ce40e49a 100644 --- a/src/ast/rewriter/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -20,9 +20,11 @@ Revision History: #include "expr_safe_replace.h" #include "rewriter.h" +#include "ast_pp.h" void expr_safe_replace::insert(expr* src, expr* dst) { + SASSERT(m.get_sort(src) == m.get_sort(dst)); m_src.push_back(src); m_dst.push_back(dst); m_subst.insert(src, dst); @@ -30,7 +32,7 @@ void expr_safe_replace::insert(expr* src, expr* dst) { void expr_safe_replace::operator()(expr* e, expr_ref& res) { m_todo.push_back(e); - expr* a, *b, *d; + expr* a, *b; while (!m_todo.empty()) { a = m_todo.back(); @@ -39,7 +41,7 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { } else if (m_subst.find(a, b)) { m_cache.insert(a, b); - m_todo.pop_back(); + m_todo.pop_back(); } else if (is_var(a)) { m_cache.insert(a, a); @@ -51,18 +53,21 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { m_args.reset(); bool arg_differs = false; for (unsigned i = 0; i < n; ++i) { - if (m_cache.find(c->get_arg(i), d)) { + expr* d = 0, *arg = c->get_arg(i); + if (m_cache.find(arg, d)) { m_args.push_back(d); - arg_differs |= c->get_arg(i) != d; + arg_differs |= arg != d; + SASSERT(m.get_sort(arg) == m.get_sort(d)); } else { - m_todo.push_back(c->get_arg(i)); + m_todo.push_back(arg); } } if (m_args.size() == n) { if (arg_differs) { b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr()); m_refs.push_back(b); + SASSERT(m.get_sort(a) == m.get_sort(b)); } else { b = a; } @@ -71,6 +76,7 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { } } else { + (std::cout << "q\n").flush(); SASSERT(is_quantifier(a)); quantifier* q = to_quantifier(a); expr_safe_replace replace(m); diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 75e27c081..ea5bec231 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -163,6 +163,7 @@ std::ostream& zstring::operator<<(std::ostream& out) const { seq_decl_plugin::seq_decl_plugin(): m_init(false), m_stringc_sym("String"), + m_charc_sym("Char"), m_string(0), m_char(0) {} @@ -369,7 +370,8 @@ void seq_decl_plugin::init() { void seq_decl_plugin::set_manager(ast_manager* m, family_id id) { decl_plugin::set_manager(m, id); - m_char = m->mk_sort(symbol("Char"), sort_info(m_family_id, _CHAR_SORT, 0, (parameter const*)0)); + bv_util bv(*m); + m_char = bv.mk_sort(8); m->inc_ref(m_char); parameter param(m_char); m_string = m->mk_sort(symbol("String"), sort_info(m_family_id, SEQ_SORT, 1, ¶m)); @@ -401,8 +403,6 @@ sort * seq_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter return m.mk_sort(symbol("RegEx"), sort_info(m_family_id, RE_SORT, num_parameters, parameters)); case _STRING_SORT: return m_string; - case _CHAR_SORT: - return m_char; default: UNREACHABLE(); return 0; @@ -470,6 +470,8 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters)); + + case OP_STRING_CONST: if (!(num_parameters == 1 && arity == 0 && parameters[0].is_symbol())) { m.raise_exception("invalid string declaration"); @@ -583,7 +585,7 @@ void seq_decl_plugin::get_sort_names(svector & sort_names, symbol app* seq_decl_plugin::mk_string(symbol const& s) { parameter param(s); func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string, - func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); + func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); return m_manager->mk_const(f); } @@ -591,10 +593,11 @@ app* seq_decl_plugin::mk_string(zstring const& s) { symbol sym(s.encode().c_str()); parameter param(sym); func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string, - func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); + func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); return m_manager->mk_const(f); } + bool seq_decl_plugin::is_value(app* e) const { return is_app_of(e, m_family_id, OP_STRING_CONST); } @@ -609,11 +612,21 @@ app* seq_util::mk_skolem(symbol const& name, unsigned n, expr* const* args, sort app* seq_util::str::mk_string(zstring const& s) { return u.seq.mk_string(s); } + app* seq_util::str::mk_char(zstring const& s, unsigned idx) { bv_util bvu(m); return bvu.mk_numeral(s[idx], s.num_bits()); } +bool seq_util::str::is_char(expr* n, zstring& c) const { + if (u.is_char(n)) { + c = zstring(to_app(n)->get_decl()->get_parameter(0).get_symbol().bare_str()); + return true; + } + else { + return false; + } +} bool seq_util::str::is_string(expr const* n, zstring& s) const { if (is_string(n)) { diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 33d4de378..9a5f65d05 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -22,13 +22,13 @@ Revision History: #define SEQ_DECL_PLUGIN_H_ #include "ast.h" +#include "bv_decl_plugin.h" enum seq_sort_kind { SEQ_SORT, RE_SORT, - _STRING_SORT, // internal only - _CHAR_SORT // internal only + _STRING_SORT // internal only }; enum seq_op_kind { @@ -131,6 +131,7 @@ class seq_decl_plugin : public decl_plugin { ptr_vector m_sigs; bool m_init; symbol m_stringc_sym; + symbol m_charc_sym; sort* m_string; sort* m_char; @@ -187,6 +188,7 @@ public: ast_manager& get_manager() const { return m; } + bool is_char(sort* s) const { return seq.is_char(s); } bool is_string(sort* s) const { return is_seq(s) && seq.is_char(s->get_parameter(0).get_ast()); } bool is_seq(sort* s) const { return is_sort_of(s, m_fid, SEQ_SORT); } bool is_re(sort* s) const { return is_sort_of(s, m_fid, RE_SORT); } @@ -195,6 +197,7 @@ public: bool is_seq(sort* s, sort*& seq) { return is_seq(s) && (seq = to_sort(s->get_parameter(0).get_ast()), true); } bool is_re(expr* e) const { return is_re(m.get_sort(e)); } bool is_re(expr* e, sort*& seq) const { return is_re(m.get_sort(e), seq); } + bool is_char(expr* e) const { return is_char(m.get_sort(e)); } app* mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range); bool is_skolem(expr const* e) const { return is_app_of(e, m_fid, _OP_SEQ_SKOLEM); } @@ -215,6 +218,7 @@ public: app* mk_empty(sort* s) { return m.mk_const(m.mk_func_decl(m_fid, OP_SEQ_EMPTY, 0, 0, 0, (expr*const*)0, s)); } app* mk_string(zstring const& s); app* mk_string(symbol const& s) { return u.seq.mk_string(s); } + app* mk_char(char ch); app* mk_concat(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); } app* mk_concat(expr* a, expr* b, expr* c) { return mk_concat(mk_concat(a, b), c); @@ -238,6 +242,7 @@ public: } bool is_string(expr const* n, zstring& s) const; + bool is_char(expr* n, zstring& s) const; bool is_empty(expr const* n) const { symbol s; return is_app_of(n, m_fid, OP_SEQ_EMPTY) || (is_string(n, s) && !s.is_numerical() && *s.bare_str() == 0); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index db96f3d0c..62e4f4338 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -331,7 +331,7 @@ bool theory_seq::check_length_coherence_tbd() { if (is_var(f) && f == e) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); TRACE("seq", tout << "Unsolved " << mk_pp(e, m) << "\n";); -#if 0 +#if 1 if (!assume_equality(e, emp)) { // e = emp \/ e = unit(head.elem(e))*tail(e) sort* char_sort = 0; @@ -341,9 +341,11 @@ bool theory_seq::check_length_coherence_tbd() { expr_ref head(m_util.str.mk_unit(v), m); expr_ref conc(m_util.str.mk_concat(head, tail), m); add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); + assume_equality(tail, emp); } #endif - coherent = false; + m_branch_variable_head = j + 1; + return false; } } return coherent; diff --git a/src/smt/theory_seq_empty.h b/src/smt/theory_seq_empty.h index 04f38020c..5065c733d 100644 --- a/src/smt/theory_seq_empty.h +++ b/src/smt/theory_seq_empty.h @@ -30,6 +30,7 @@ namespace smt { seq_util u; symbol_set m_strings; unsigned m_next; + char m_char; std::string m_unique_prefix; obj_map m_unique_sequences; expr_ref_vector m_trail; @@ -41,6 +42,7 @@ namespace smt { m_model(md), u(m), m_next(0), + m_char(0), m_unique_prefix("#B"), m_trail(m) { @@ -99,6 +101,11 @@ namespace smt { expr* v0 = get_fresh_value(seq); return u.re.mk_to_re(v0); } + if (u.is_char(s)) { + //char s[2] = { ++m_char, 0 }; + //return u.str.mk_char(zstring(s), 0); + return u.str.mk_char(zstring("a"), 0); + } NOT_IMPLEMENTED_YET(); return 0; } diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 06a82b0d5..a22275043 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -239,16 +239,26 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode else { scoped_mpq sig(m_mpq_manager); scoped_mpz exp(m_mpq_manager); + scoped_mpq pow(m_mpq_manager); m_mpq_manager.set(sig, significand); m_mpq_manager.abs(sig); m_mpz_manager.set(exp, exponent); - + m_mpq_manager.set(pow, mpq(2)); + // Normalize - while (m_mpq_manager.ge(sig, 2)) { - m_mpq_manager.div(sig, mpq(2), sig); + unsigned loop = 0; + while (m_mpq_manager.ge(sig, pow)) { + m_mpq_manager.mul(pow, 2, pow); m_mpz_manager.inc(exp); + ++loop; + if (loop % 1000 == 0) std::cout << loop << "\n"; } + std::cout << loop << "\n"; + if (loop > 0) { + m_mpq_manager.div(sig, pow, sig); + } + std::cout << loop << "\n"; while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); From 995d66c6f2b8f10943e9406eb6035218ce5f7166 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 22 Dec 2015 10:46:33 -0800 Subject: [PATCH 05/35] remove print statements Signed-off-by: Nikolaj Bjorner --- src/api/api_ast_vector.cpp | 1 - src/util/mpf.cpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/api/api_ast_vector.cpp b/src/api/api_ast_vector.cpp index d6ff1bbb3..e1d4d78ff 100644 --- a/src/api/api_ast_vector.cpp +++ b/src/api/api_ast_vector.cpp @@ -26,7 +26,6 @@ Revision History: extern "C" { Z3_ast_vector Z3_API Z3_mk_ast_vector(Z3_context c) { - std::cout << "ast-vector\n"; Z3_TRY; LOG_Z3_mk_ast_vector(c); RESET_ERROR_CODE(); diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index a22275043..6cf8e3a4d 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -252,13 +252,10 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode m_mpq_manager.mul(pow, 2, pow); m_mpz_manager.inc(exp); ++loop; - if (loop % 1000 == 0) std::cout << loop << "\n"; } - std::cout << loop << "\n"; if (loop > 0) { m_mpq_manager.div(sig, pow, sig); } - std::cout << loop << "\n"; while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); From 72d2cd546eb0da474ae28d8a2933d219cb1f713e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 22 Dec 2015 17:48:02 -0800 Subject: [PATCH 06/35] elim_bounds bugfix Signed-off-by: Nikolaj Bjorner --- src/ast/simplifier/elim_bounds.cpp | 6 ++++-- src/tactic/smtlogics/quant_tactics.cpp | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ast/simplifier/elim_bounds.cpp b/src/ast/simplifier/elim_bounds.cpp index 41ce18549..a4e145e0a 100644 --- a/src/ast/simplifier/elim_bounds.cpp +++ b/src/ast/simplifier/elim_bounds.cpp @@ -110,6 +110,7 @@ void elim_bounds::operator()(quantifier * q, expr_ref & r) { return; } expr * n = q->get_expr(); + unsigned num_vars = q->get_num_decls(); ptr_buffer atoms; if (m_manager.is_or(n)) atoms.append(to_app(n)->get_num_args(), to_app(n)->get_args()); @@ -138,11 +139,11 @@ void elim_bounds::operator()(quantifier * q, expr_ref & r) { var * lower = 0; var * upper = 0; if (is_bound(a, lower, upper)) { - if (lower != 0 && !m_used_vars.contains(lower->get_idx())) { + if (lower != 0 && !m_used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) { ADD_CANDIDATE(lower); m_lowers.insert(lower); } - if (upper != 0 && !m_used_vars.contains(upper->get_idx())) { + if (upper != 0 && !m_used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) { ADD_CANDIDATE(upper); m_uppers.insert(upper); } @@ -176,6 +177,7 @@ void elim_bounds::operator()(quantifier * q, expr_ref & r) { switch (atoms.size()) { case 0: r = m_manager.mk_false(); + TRACE("elim_bounds", tout << mk_pp(q, m_manager) << "\n" << mk_pp(r, m_manager) << "\n";); return; case 1: new_body = atoms[0]; diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index 937b0229e..8714b055f 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -102,7 +102,8 @@ tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { or_else(try_for(mk_smt_tactic(), 100), try_for(qe::mk_sat_tactic(m), 1000), try_for(mk_smt_tactic(), 1000), - and_then(mk_qe_tactic(m), mk_smt_tactic()))); + and_then(mk_qe_tactic(m), mk_smt_tactic()) + )); st->updt_params(p); return st; From 386399472dbfa6e78d0c4d77502b9cc025bbd51f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Dec 2015 11:02:34 -0800 Subject: [PATCH 07/35] seq Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 89 +++++++++++++++++++++--------- src/ast/seq_decl_plugin.cpp | 17 +++++- src/smt/smt_model_generator.cpp | 4 +- src/smt/theory_seq.cpp | 98 +++++++++++++++++++++++---------- src/smt/theory_seq.h | 17 ++++-- 5 files changed, 160 insertions(+), 65 deletions(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 09e4bd3a9..9a9b17500 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -87,6 +87,10 @@ struct (z3obj_sno res ctx no) ; (z3obj_create res) ; res + + let ignore2 a b c = ignore a; ignore b + let ignore3 a b c = ignore a; ignore2 b c + end open Internal @@ -833,6 +837,22 @@ end = struct let o = Z3native.mk_app (context_gno ctx) (AST.ptr_of_ast fa) (List.length args) (expr_lton args) in expr_of_ptr ctx o + let apply_un ctx f t = + let r = expr_of_ptr ctx (f (context_gno ctx) (gno t)) in + ignore t; + r + + let apply_bin ctx f t1 t2 = + let r = expr_of_ptr ctx (f (context_gno ctx) (gno t1) (gno t2)) in + ignore2 t1 t2; + r + + let apply_ter ctx f t1 t2 t3 = + let r = expr_of_ptr ctx (f (context_gno ctx) (gno t1) (gno t2) (gno t3)) in + ignore3 t1 t2 t3; + r + + let simplify ( x : expr ) ( p : Params.params option ) = match p with | None -> expr_of_ptr (Expr.gc x) (Z3native.simplify (gnc x) (gno x)) | Some pp -> expr_of_ptr (Expr.gc x) (Z3native.simplify_ex (gnc x) (gno x) (z3obj_gno pp)) @@ -854,13 +874,17 @@ end = struct if ((AST.is_app (ast_of_expr x)) && (List.length args <> (get_num_args x))) then raise (Z3native.Exception "Number of arguments does not match") else - expr_of_ptr (Expr.gc x) (Z3native.update_term (gnc x) (gno x) (List.length args) (expr_lton args)) + let r = expr_of_ptr (Expr.gc x) (Z3native.update_term (gnc x) (gno x) (List.length args) (expr_lton args)) in + ignore2 x args; + r let substitute ( x : expr ) from to_ = if (List.length from) <> (List.length to_) then raise (Z3native.Exception "Argument sizes do not match") else - expr_of_ptr (Expr.gc x) (Z3native.substitute (gnc x) (gno x) (List.length from) (expr_lton from) (expr_lton to_)) + let r = expr_of_ptr (Expr.gc x) (Z3native.substitute (gnc x) (gno x) (List.length from) (expr_lton from) (expr_lton to_)) in + ignore3 from to_ x; + r let substitute_one ( x : expr ) from to_ = substitute ( x : expr ) [ from ] [ to_ ] @@ -872,7 +896,9 @@ end = struct if (Expr.gc x) == to_ctx then x else - expr_of_ptr to_ctx (Z3native.translate (gnc x) (gno x) (context_gno to_ctx)) + let r = expr_of_ptr to_ctx (Z3native.translate (gnc x) (gno x) (context_gno to_ctx)) in + ignore2 x to_ctx; + r let to_string ( x : expr ) = Z3native.ast_to_string (gnc x) (gno x) @@ -933,34 +959,39 @@ struct let mk_val ( ctx : context ) ( value : bool ) = if value then mk_true ctx else mk_false ctx - let mk_not ( ctx : context ) ( a : expr ) = - expr_of_ptr ctx (Z3native.mk_not (context_gno ctx) (gno a)) + let mk_not ( ctx : context ) ( a : expr ) = apply_un ctx Z3native.mk_not a - let mk_ite ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( t3 : expr ) = - expr_of_ptr ctx (Z3native.mk_ite (context_gno ctx) (gno t1) (gno t2) (gno t3)) + let mk_ite ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( t3 : expr ) = + apply_ter ctx Z3native.mk_ite t1 t2 t3 let mk_iff ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - expr_of_ptr ctx (Z3native.mk_iff (context_gno ctx) (gno t1) (gno t2)) + apply_bin ctx Z3native.mk_iff t1 t2 let mk_implies ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - expr_of_ptr ctx (Z3native.mk_implies (context_gno ctx) (gno t1) (gno t2)) + apply_bin ctx Z3native.mk_implies t1 t2 let mk_xor ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - expr_of_ptr ctx (Z3native.mk_xor (context_gno ctx) (gno t1) (gno t2)) + apply_bin ctx Z3native.mk_xor t1 t2 let mk_and ( ctx : context ) ( args : expr list ) = let f x = (Expr.gno (x)) in - expr_of_ptr ctx (Z3native.mk_and (context_gno ctx) (List.length args) (Array.of_list (List.map f args))) + let r = expr_of_ptr ctx (Z3native.mk_and (context_gno ctx) (List.length args) (Array.of_list (List.map f args))) in + ignore args; + r let mk_or ( ctx : context ) ( args : expr list ) = let f x = (Expr.gno (x)) in - expr_of_ptr ctx (Z3native.mk_or (context_gno ctx) (List.length args) (Array.of_list(List.map f args))) + let r = expr_of_ptr ctx (Z3native.mk_or (context_gno ctx) (List.length args) (Array.of_list(List.map f args))) in + ignore args; + r let mk_eq ( ctx : context ) ( x : expr ) ( y : expr ) = - expr_of_ptr ctx (Z3native.mk_eq (context_gno ctx) (Expr.gno x) (Expr.gno y)) + apply_bin ctx Z3native.mk_eq x y let mk_distinct ( ctx : context ) ( args : expr list ) = - expr_of_ptr ctx (Z3native.mk_distinct (context_gno ctx) (List.length args) (expr_lton args)) + let r = expr_of_ptr ctx (Z3native.mk_distinct (context_gno ctx) (List.length args) (expr_lton args)) in + ignore args; + r let get_bool_value ( x : expr ) = lbool_of_int (Z3native.get_bool_value (gnc x) (gno x)) @@ -1066,10 +1097,12 @@ struct mk_list f n let get_body ( x : quantifier ) = - expr_of_ptr (gc x) (Z3native.get_quantifier_body (gnc x) (gno x)) + apply_un (gc x) Z3native.get_quantifier_body x let mk_bound ( ctx : context ) ( index : int ) ( ty : Sort.sort ) = - expr_of_ptr ctx (Z3native.mk_bound (context_gno ctx) index (Sort.gno ty)) + let r = expr_of_ptr ctx (Z3native.mk_bound (context_gno ctx) index (Sort.gno ty)) in + ignore ty; + r let mk_pattern ( ctx : context ) ( terms : expr list ) = if (List.length terms) == 0 then @@ -1194,23 +1227,27 @@ struct mk_const ctx (Symbol.mk_string ctx name) domain range let mk_select ( ctx : context ) ( a : expr ) ( i : expr ) = - expr_of_ptr ctx (Z3native.mk_select (context_gno ctx) (Expr.gno a) (Expr.gno i)) + apply_bin ctx Z3native.mk_select a i let mk_store ( ctx : context ) ( a : expr ) ( i : expr ) ( v : expr ) = - expr_of_ptr ctx (Z3native.mk_store (context_gno ctx) (Expr.gno a) (Expr.gno i) (Expr.gno v)) + apply_ter ctx Z3native.mk_store a i v let mk_const_array ( ctx : context ) ( domain : Sort.sort ) ( v : expr ) = - expr_of_ptr ctx (Z3native.mk_const_array (context_gno ctx) (Sort.gno domain) (Expr.gno v)) + let r = expr_of_ptr ctx (Z3native.mk_const_array (context_gno ctx) (Sort.gno domain) (Expr.gno v)) in + ignore2 domain v; + r let mk_map ( ctx : context ) ( f : func_decl ) ( args : expr list ) = let m x = (Expr.gno x) in - expr_of_ptr ctx (Z3native.mk_map (context_gno ctx) (FuncDecl.gno f) (List.length args) (Array.of_list (List.map m args))) + let r = expr_of_ptr ctx (Z3native.mk_map (context_gno ctx) (FuncDecl.gno f) (List.length args) (Array.of_list (List.map m args))) in + ignore2 f args; + r let mk_term_array ( ctx : context ) ( arg : expr ) = - expr_of_ptr ctx (Z3native.mk_array_default (context_gno ctx) (Expr.gno arg)) + apply_un ctx Z3native.mk_array_default arg let mk_array_ext ( ctx : context) ( arg1 : expr ) ( arg2 : expr ) = - expr_of_ptr ctx (Z3native.mk_array_ext (context_gno ctx) (Expr.gno arg1) (Expr.gno arg2)) + apply_bin ctx Z3native.mk_array_ext arg1 arg2 end @@ -1233,13 +1270,15 @@ struct expr_of_ptr ctx (Z3native.mk_full_set (context_gno ctx) (Sort.gno domain)) let mk_set_add ( ctx : context ) ( set : expr ) ( element : expr ) = - expr_of_ptr ctx (Z3native.mk_set_add (context_gno ctx) (Expr.gno set) (Expr.gno element)) + apply_bin ctx Z3native.mk_set_add set element let mk_del ( ctx : context ) ( set : expr ) ( element : expr ) = - expr_of_ptr ctx (Z3native.mk_set_del (context_gno ctx) (Expr.gno set) (Expr.gno element)) + apply_bin Z3native.mk_set_del set element let mk_union ( ctx : context ) ( args : expr list ) = - expr_of_ptr ctx (Z3native.mk_set_union (context_gno ctx) (List.length args) (expr_lton args)) + let r = expr_of_ptr ctx (Z3native.mk_set_union (context_gno ctx) (List.length args) (expr_lton args)) in + ignore r; + r let mk_intersection ( ctx : context ) ( args : expr list ) = expr_of_ptr ctx (Z3native.mk_set_intersect (context_gno ctx) (List.length args) (expr_lton args)) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index ea5bec231..5b5ed1c55 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -42,7 +42,7 @@ zstring::zstring(unsigned num_bits, bool const* ch) { m_encoding = (num_bits == 8)?ascii:unicode; unsigned n = 0; for (unsigned i = 0; i < num_bits; ++i) { - n |= (((unsigned)ch[i]) << num_bits); + n |= (((unsigned)ch[i]) << i); } m_buffer.push_back(n); } @@ -81,12 +81,23 @@ zstring zstring::replace(zstring const& src, zstring const& dst) const { return result; } +static char* esc_table[32] = { "\\0", "^A", "^B", "^C", "^D", "^E", "^F", "\\a", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "^N", + "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V","^W","^X","^Y","^Z","\\e","^\\","^]","^^","^_"}; + std::string zstring::encode() const { - // TBD apply encodings. SASSERT(m_encoding == ascii); std::ostringstream strm; for (unsigned i = 0; i < m_buffer.size(); ++i) { - strm << (char)(m_buffer[i]); + unsigned char ch = m_buffer[i]; + if (0 <= ch && ch < 32) { + strm << esc_table[ch]; + } + else if (ch == 127) { + strm << "^?"; + } + else { + strm << (char)(ch); + } } return strm.str(); } diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 98d982b7a..16cf18d2e 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -90,7 +90,9 @@ namespace smt { sort * s = m_manager.get_sort(r->get_owner()); model_value_proc * proc = 0; if (m_manager.is_bool(s)) { - SASSERT(m_context->get_assignment(r) == l_true || m_context->get_assignment(r) == l_false); + CTRACE("func_interp_bug", m_context->get_assignment(r) == l_undef, + tout << mk_pp(r->get_owner(), m_manager) << "\n";); + SASSERT(m_context->get_assignment(r) != l_undef); if (m_context->get_assignment(r) == l_true) proc = alloc(expr_wrapper_proc, m_manager.mk_true()); else diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 62e4f4338..79779d088 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -132,6 +132,7 @@ theory_seq::theory_seq(ast_manager& m): m_incomplete(false), m_has_length(false), m_model_completion(false), + m_mg(0), m_rewrite(m), m_util(m), m_autil(m), @@ -181,7 +182,7 @@ final_check_status theory_seq::final_check_eh() { } if (!check_length_coherence_tbd()) { TRACE("seq", tout << "check_length_coherence\n";); - return FC_GIVEUP; + return FC_CONTINUE; } if (is_solved()) { TRACE("seq", tout << "is_solved\n";); @@ -400,15 +401,15 @@ void theory_seq::propagate_lit(enode_pair_dependency* dep, literal lit) { ctx.assign(lit, js); } -void theory_seq::set_conflict(enode_pair_dependency* dep) { +void theory_seq::set_conflict(enode_pair_dependency* dep, literal_vector const& lits) { context& ctx = get_context(); vector _eqs; m_dm.linearize(dep, _eqs); - TRACE("seq", display_deps(tout, dep);); + TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); display_deps(tout, dep); ;); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( - get_id(), ctx.get_region(), 0, 0, _eqs.size(), _eqs.c_ptr(), 0, 0))); + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), _eqs.size(), _eqs.c_ptr(), 0, 0))); } void theory_seq::propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2) { @@ -524,6 +525,10 @@ bool theory_seq::is_right_select(expr* a, expr*& b) { to_app(a)->get_decl()->get_parameter(0).get_symbol() == m_right_sym && (b = to_app(a)->get_arg(0), true); } +bool theory_seq::is_head_elem(expr* e) const { + return m_util.is_skolem(e) && to_app(e)->get_decl()->get_parameter(0).get_symbol() == symbol("seq.head.elem"); +} + void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { context& ctx = get_context(); m_rep.update(l, r, deps); @@ -583,17 +588,17 @@ bool theory_seq::solve_ne(unsigned idx) { TRACE("seq", display_disequation(tout, n);); SASSERT(!n.is_solved()); + unsigned num_undef_lits = 0; for (unsigned i = 0; i < n.m_lits.size(); ++i) { switch (ctx.get_assignment(n.m_lits[i])) { - case l_true: - erase_lit(idx, i); - --i; - break; case l_false: // mark as solved in mark_solved(idx); return false; + case l_true: + break; case l_undef: + ++num_undef_lits; break; } } @@ -630,29 +635,25 @@ bool theory_seq::solve_ne(unsigned idx) { } else { literal lit(mk_eq(nl, nr, false)); - m_trail_stack.push(push_lit(*this, idx, ~lit)); + m_trail_stack.push(push_lit(*this, idx, lit)); ctx.mark_as_relevant(lit); } } m_trail_stack.push(push_dep(*this, idx, deps)); erase_index(idx, i); --i; + change = true; } } - if (n.m_lits.empty() && n.m_lhs.empty()) { - set_conflict(n.m_dep); + if (num_undef_lits == 0 && n.m_lhs.empty()) { + literal_vector lits(n.m_lits); + lits.push_back(~mk_eq(n.m_l, n.m_r, false)); + set_conflict(n.m_dep, lits); return true; } return change; } -void theory_seq::erase_lit(unsigned idx, unsigned i) { - ne const& n = m_nqs[idx]; - if (n.m_lits.size() < i + 1) { - m_trail_stack.push(set_lit(*this, idx, i, n.m_lits.back())); - } - m_trail_stack.push(pop_lit(*this, idx)); -} void theory_seq::mark_solved(unsigned idx) { m_trail_stack.push(solved_ne(*this, idx)); @@ -810,8 +811,12 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { enode_pair_dependency* deps = 0; expr_ref e(n->get_owner(), m); flet _model_completion(m_model_completion, true); + m_rep.reset_cache(); + m_mg = &mg; e = canonize(e, deps); + m_mg = 0; SASSERT(is_app(e)); + TRACE("seq", tout << mk_pp(n->get_owner(), m) << " -> " << e << "\n";); m_factory->add_trail(e); return alloc(expr_wrapper_proc, to_app(e)); } @@ -852,15 +857,17 @@ expr_ref theory_seq::canonize(expr* e, enode_pair_dependency*& eqs) { return result; } -expr_ref theory_seq::expand(expr* e, enode_pair_dependency*& eqs) { +expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { + expr_ref result(m); enode_pair_dependency* deps = 0; expr_dep ed; - if (m_rep.find_cache(e, ed)) { + if (m_rep.find_cache(e0, ed)) { eqs = m_dm.mk_join(eqs, ed.second); - return expr_ref(ed.first, m); + result = ed.first; + TRACE("seq", tout << mk_pp(e0, m) << " |-> " << result << " "; display_deps(tout, eqs);); + return result; } - e = m_rep.find(e, deps); - expr_ref result(m); + expr* e = m_rep.find(e0, deps); expr* e1, *e2; if (m_util.str.is_concat(e, e1, e2)) { result = m_util.str.mk_concat(expand(e1, deps), expand(e2, deps)); @@ -889,15 +896,43 @@ expr_ref theory_seq::expand(expr* e, enode_pair_dependency*& eqs) { result = e; } } + else if (m_model_completion && m_util.str.is_unit(e, e1)) { + result = expand(e1, deps); + bv_util bv(m); + rational val; + unsigned sz; + if (bv.is_numeral(result, val, sz) && sz == zstring().num_bits()) { + svector val_as_bits; + for (unsigned i = 0; i < sz; ++i) { + val_as_bits.push_back(!val.is_even()); + val = div(val, rational(2)); + } + result = m_util.str.mk_string(zstring(sz, val_as_bits.c_ptr())); + } + else { + result = m_util.str.mk_unit(result); + } + } + else if (m_model_completion && is_head_elem(e)) { + enode* n = get_context().get_enode(e)->get_root(); + result = n->get_owner(); + if (!m.is_model_value(result)) { + result = m_mg->get_some_value(m.get_sort(result)); + } + m_rep.update(e, result, 0); + TRACE("seq", tout << mk_pp(e, m) << " |-> " << result << "\n";); + } else { result = e; } - if (result == e) { + if (result == e0) { deps = 0; } expr_dep edr(result, deps); - m_rep.add_cache(e, edr); + m_rep.add_cache(e0, edr); eqs = m_dm.mk_join(eqs, deps); + TRACE("seq", tout << mk_pp(e0, m) << " |--> " << result << "\n"; + display_deps(tout, eqs);); return result; } @@ -1116,8 +1151,9 @@ void theory_seq::add_length_concat_axiom(expr* n) { expr_ref len(m_util.str.mk_length(n), m); expr_ref _a(m_util.str.mk_length(a), m); expr_ref _b(m_util.str.mk_length(b), m); - expr_ref a_p_b(m_autil.mk_add(_a, _b), m); - add_axiom(mk_eq(len, a_p_b, false)); + expr_ref ab(m_autil.mk_add(_a, _b), m); + m_rewrite(ab); + add_axiom(mk_eq(ab, len, false)); } /* @@ -1144,8 +1180,10 @@ void theory_seq::add_length_axiom(expr* n) { } } -expr* theory_seq::mk_sub(expr* a, expr* b) { - return m_autil.mk_add(a, m_autil.mk_mul(m_autil.mk_int(-1), b)); +expr_ref theory_seq::mk_sub(expr* a, expr* b) { + expr_ref result(m_autil.mk_sub(a, b), m); + m_rewrite(result); + return result; } enode* theory_seq::ensure_enode(expr* e) { @@ -1325,7 +1363,7 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { enode* n2 = get_enode(v2); expr_ref e1(n1->get_owner(), m); expr_ref e2(n2->get_owner(), m); - m_nqs.push_back(ne(e1, e2, m_dm.mk_leaf(enode_pair(n1, n2)))); + m_nqs.push_back(ne(e1, e2)); m_exclude.update(e1, e2); } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 59ae5095a..21aabb599 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -70,6 +70,7 @@ namespace smt { bool find_cache(expr* v, expr_dep& r) { return m_cache.find(v, r); } expr* find(expr* e, enode_pair_dependency*& d); void cache(expr* e, expr* r, enode_pair_dependency* d); + void reset_cache() { m_cache.reset(); } void push_scope() { m_limit.push_back(m_updates.size()); } void pop_scope(unsigned num_scopes); void display(std::ostream& out) const; @@ -108,19 +109,22 @@ namespace smt { // asserted or derived disqequality with dependencies struct ne { bool m_solved; + expr_ref m_l, m_r; expr_ref_vector m_lhs; expr_ref_vector m_rhs; literal_vector m_lits; enode_pair_dependency* m_dep; - ne(expr_ref& l, expr_ref& r, enode_pair_dependency* d): - m_solved(false), m_lhs(l.get_manager()), m_rhs(r.get_manager()), m_dep(d) { + ne(expr_ref& l, expr_ref& r): + m_solved(false), m_l(l), m_r(r), m_lhs(l.get_manager()), m_rhs(r.get_manager()), m_dep(0) { m_lhs.push_back(l); m_rhs.push_back(r); } ne(ne const& other): - m_solved(other.m_solved), m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_lits(other.m_lits), m_dep(other.m_dep) {} + m_solved(other.m_solved), m_l(other.m_l), m_r(other.m_r), m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_lits(other.m_lits), m_dep(other.m_dep) {} ne& operator=(ne const& other) { m_solved = other.m_solved; + m_l = other.m_l; + m_r = other.m_r; m_lhs.reset(); m_lhs.append(other.m_lhs); m_rhs.reset(); m_rhs.append(other.m_rhs); m_lits.reset(); m_lits.append(other.m_lits); @@ -158,7 +162,6 @@ namespace smt { } virtual void undo(theory_seq & th) { th.m_nqs.ref(m_idx).m_lits[m_i] = m_lit; } }; - void erase_lit(unsigned idx, unsigned i); class solved_ne : public trail { unsigned m_idx; @@ -255,6 +258,7 @@ namespace smt { bool m_incomplete; // is the solver (clearly) incomplete for the fragment. bool m_has_length; // is length applied bool m_model_completion; // during model construction, invent values in canonizer + model_generator* m_mg; th_rewriter m_rewrite; seq_util m_util; arith_util m_autil; @@ -313,7 +317,7 @@ namespace smt { void propagate_lit(enode_pair_dependency* dep, literal lit); void propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2); void propagate_eq(bool_var v, expr* e1, expr* e2); - void set_conflict(enode_pair_dependency* dep); + void set_conflict(enode_pair_dependency* dep, literal_vector const& lits = literal_vector()); bool find_branch_candidate(expr* l, expr_ref_vector const& rs); bool assume_equality(expr* l, expr* r); @@ -324,6 +328,7 @@ namespace smt { void add_solution(expr* l, expr* r, enode_pair_dependency* dep); bool is_left_select(expr* a, expr*& b); bool is_right_select(expr* a, expr*& b); + bool is_head_elem(expr* a) const; expr_ref canonize(expr* e, enode_pair_dependency*& eqs); expr_ref expand(expr* e, enode_pair_dependency*& eqs); void add_dependency(enode_pair_dependency*& dep, enode* a, enode* b); @@ -345,7 +350,7 @@ namespace smt { void add_at_axiom(expr* n); literal mk_literal(expr* n); void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal); - expr* mk_sub(expr* a, expr* b); + expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); From f4148694563af27f27050fe34b7e0513a8dba98b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Dec 2015 19:46:10 -0800 Subject: [PATCH 08/35] add symbolic automaton Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 3 +- src/ast/rewriter/seq_rewriter.cpp | 59 ++++-- src/ast/seq_decl_plugin.cpp | 5 + src/math/automata/automaton.cpp | 23 +++ src/math/automata/automaton.h | 295 ++++++++++++++++++++++++++++++ src/smt/smt_context.cpp | 4 +- src/smt/theory_seq.cpp | 68 ++++++- src/smt/theory_seq.h | 3 +- 8 files changed, 428 insertions(+), 32 deletions(-) create mode 100644 src/math/automata/automaton.cpp create mode 100644 src/math/automata/automaton.h diff --git a/scripts/mk_project.py b/scripts/mk_project.py index eb7218d4b..08986d96b 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -16,11 +16,12 @@ def init_project_def(): add_lib('nlsat', ['polynomial', 'sat']) add_lib('hilbert', ['util'], 'math/hilbert') add_lib('simplex', ['util'], 'math/simplex') + add_lib('automata', ['util'], 'math/automata') add_lib('interval', ['util'], 'math/interval') add_lib('realclosure', ['interval'], 'math/realclosure') add_lib('subpaving', ['interval'], 'math/subpaving') add_lib('ast', ['util', 'polynomial']) - add_lib('rewriter', ['ast', 'polynomial'], 'ast/rewriter') + add_lib('rewriter', ['ast', 'polynomial', 'automata'], 'ast/rewriter') add_lib('normal_forms', ['rewriter'], 'ast/normal_forms') add_lib('model', ['rewriter']) add_lib('tactic', ['ast', 'model']) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index d7ac3ff67..41c20f599 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -22,6 +22,7 @@ Notes: #include"ast_pp.h" #include"ast_util.h" #include"uint_set.h" +#include"automaton.h" br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { @@ -30,22 +31,38 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con switch(f->get_decl_kind()) { case OP_SEQ_UNIT: - case OP_SEQ_EMPTY: - - case OP_RE_PLUS: - case OP_RE_STAR: - case OP_RE_OPTION: - case OP_RE_RANGE: - case OP_RE_CONCAT: - case OP_RE_UNION: - case OP_RE_INTERSECT: - case OP_RE_LOOP: - case OP_RE_EMPTY_SET: - case OP_RE_FULL_SET: - case OP_RE_OF_PRED: - case _OP_SEQ_SKOLEM: return BR_FAILED; - + case OP_SEQ_EMPTY: + return BR_FAILED; + case OP_RE_PLUS: + SASSERT(num_args == 1); + return mk_re_plus(args[0], result); + case OP_RE_STAR: + SASSERT(num_args == 1); + return mk_re_star(args[0], result); + case OP_RE_OPTION: + SASSERT(num_args == 1); + return mk_re_opt(args[0], result); + case OP_RE_CONCAT: + SASSERT(num_args == 2); + return mk_re_concat(args[0], args[1], result); + case OP_RE_UNION: + SASSERT(num_args == 2); + return mk_re_union(args[0], args[1], result); + case OP_RE_RANGE: + return BR_FAILED; + case OP_RE_INTERSECT: + return BR_FAILED; + case OP_RE_LOOP: + return BR_FAILED; + case OP_RE_EMPTY_SET: + return BR_FAILED; + case OP_RE_FULL_SET: + return BR_FAILED; + case OP_RE_OF_PRED: + return BR_FAILED; + case _OP_SEQ_SKOLEM: + return BR_FAILED; case OP_SEQ_CONCAT: if (num_args == 1) { result = args[0]; @@ -83,10 +100,11 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con SASSERT(num_args == 3); return mk_seq_replace(args[0], args[1], args[2], result); case OP_SEQ_TO_RE: - return BR_FAILED; + SASSERT(num_args == 1); + return mk_str_to_regexp(args[0], result); case OP_SEQ_IN_RE: - return BR_FAILED; - + SASSERT(num_args == 2); + return mk_str_in_regexp(args[0], args[1], result); case OP_STRING_CONST: return BR_FAILED; case OP_STRING_ITOS: @@ -499,7 +517,10 @@ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { return BR_FAILED; } br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) { - return BR_FAILED; + sort* s; + VERIFY(m_util.is_re(a, s)); + result = m_util.re.mk_union(m_util.re.mk_to_re(m_util.str.mk_empty(s)), a); + return BR_REWRITE1; } br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) { diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 5b5ed1c55..10e9c00a5 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -629,6 +629,11 @@ app* seq_util::str::mk_char(zstring const& s, unsigned idx) { return bvu.mk_numeral(s[idx], s.num_bits()); } +app* seq_util::str::mk_char(char ch) { + zstring s(ch, zstring::ascii); + return mk_char(s, 0); +} + bool seq_util::str::is_char(expr* n, zstring& c) const { if (u.is_char(n)) { c = zstring(to_app(n)->get_decl()->get_parameter(0).get_symbol().bare_str()); diff --git a/src/math/automata/automaton.cpp b/src/math/automata/automaton.cpp new file mode 100644 index 000000000..ab908525e --- /dev/null +++ b/src/math/automata/automaton.cpp @@ -0,0 +1,23 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + automaton.cpp + +Abstract: + + Symbolic Automaton, a la Margus Veanes Automata library. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-12-23. + +Revision History: + + +--*/ + +#include "automaton.h" + +template class automaton; diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h new file mode 100644 index 000000000..e28db8fa0 --- /dev/null +++ b/src/math/automata/automaton.h @@ -0,0 +1,295 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + automaton.h + +Abstract: + + Symbolic Automaton, a la Margus Veanes Automata library. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-12-23. + +Revision History: + + +--*/ + +#ifndef AUTOMATON_H_ +#define AUTOMATON_H_ + + +#include "util.h" +#include "vector.h" +#include "uint_set.h" + +template +class automaton { + class move { + T* m_t; + unsigned m_src; + unsigned m_dst; + public: + move(unsigned s, unsigned d, T* t = 0): m_t(t), m_src(s), m_dst(d) {} + + unsigned dst() const { return m_dst; } + unsigned src() const { return m_src; } + T* t() const { return m_t; } + + bool is_epsilon() const { return m_t == 0; } + }; + typedef svector moves; + + + svector m_delta; + svector m_delta_inv; + unsigned m_init; + uint_set m_final_set; + unsigned_vector m_final_states; + bool m_is_epsilon_free; + bool m_is_deterministic; + + + // local data-structures + mutable uint_set m_visited; + mutable unsigned_vector m_todo; + + +public: + + // The empty automaton: + automaton(): + m_init(0), + m_is_epsilon_free(true), + m_is_deterministic(true) + { + m_delta.push_back(moves()); + m_delta_inv.push_back(moves()); + } + + + // create an automaton from initial state, final states, and moves + automaton(unsigned init, unsigned_vector const& final, moves const& mvs) { + m_is_epsilon_free = true; + m_is_deterministic = true; + m_init = init; + for (unsigned i = 0; i < final.size(); ++i) { + m_final_states.push_back(final[i]); + m_final_set.insert(final[i]); + } + for (unsigned i = 0; i < mvs.size(); ++i) { + move const& mv = mvs[i]; + unsigned n = std::max(mv.src(), mv.dst()); + if (n >= m_delta.size()) { + m_delta.resize(n+1, moves()); + m_delta_inv.resize(n+1, moves()); + } + m_delta[mv.src()].push_back(mv); + m_delta_inv[mv.dst()].push_back(mv); + m_is_deterministic &= m_delta[mv.src()].size() < 2; + m_is_epsilon_free &= !mv.is_epsilon(); + } + } + + // The automaton with a single state that is also final. + automaton(T* t, unsigned s = 0): + m_init(s) { + m_delta.resize(s+1, moves()); + m_delta_inv.resize(s+1, moves()); + m_final_set.insert(s); + m_final_states.push_back(s); + m_delta[s].push_back(move(s, s, t)); + m_delta_inv[s].push_back(move(s, s, t)); + } + + automaton(automaton const& other): + m_delta(other.m_delta), + m_delta_inv(other.m_delta_inv), + m_init(other.m_init), + m_final_set(other.m_final_set), + m_final_states(other.m_final_states), + m_is_epsilon_free(other.m_is_epsilon_free), + m_is_deterministic(other.m_is_deterministic) + {} + + // create the automaton that accepts the empty string only. + static automaton mk_epsilon() { + moves mvs; + unsigned_vector final; + final.push_back(0); + return automaton(0, final, mvs); + } + + // create the automaton with a single state on condition t. + static automaton mk_loop(T* t) { + moves mvs; + unsigned_vector final; + final.push_back(0); + mvs.push_back(move(0, 0, t)); + return automaton(0, final, mvs); + } + + // create the sum of disjoint automata + static automaton mk_union(automaton const& a, automaton const& b) { + moves mvs; + unsigned_vector final; + unsigned offset1 = 1; + unsigned offset2 = a.num_states() + 1; + mvs.push_back(move(0, a.init() + offset1, 0)); + mvs.push_back(move(0, b.init() + offset2, 0)); + append_moves(offset1, a, mvs); + append_moves(offset2, b, mvs); + append_final(offset1, a, final); + append_final(offset2, b, final); + return automaton(0, final, mvs); + } + + static automaton mk_reverse(automaton const& a) { + if (a.is_empty()) { + return automaton(); + } + moves mvs; + for (unsigned i = 0; i < a.m_delta.size(); ++i) { + moves const& mvs1 = a.m_delta[i]; + for (unsigned j = 0; j < mvs1.size(); ++j) { + move const& mv = mvs1[j]; + mvs.push_back(move(mv.dst(), mv.src(), mv.t())); + } + } + unsigned_vector final; + unsigned init; + final.push_back(a.init()); + if (a.m_final_states.size() == 1) { + init = a.m_final_states[0]; + } + else { + init = a.num_states(); + for (unsigned i = 0; i < a.m_final_states.size(); ++i) { + mvs.push_back(move(init, a.m_final_states[i])); + } + } + return automaton(init, final, mvs); + } + + bool is_sequence(unsigned& length) const { + if (is_final_state(m_init) && (out_degree(m_init) == 0 || (out_degree(m_init) == 1 && is_loop_state(m_init)))) { + length = 0; + return true; + } + if (is_empty() || in_degree(m_init) != 0 || out_degree(m_init) != 1) { + return false; + } + + length = 1; + unsigned s = get_move_from(m_init).dst(); + while (!is_final_state(s)) { + if (out_degree(s) != 1 || in_degree(s) != 1) { + return false; + } + s = get_move_from(s).dst(); + ++length; + } + return out_degree(s) == 0 || (out_degree(s) == 1 && is_loop_state(s)); + } + + unsigned init() const { return m_init; } + unsigned in_degree(unsigned state) const { return m_delta_inv[state].size(); } + unsigned out_degree(unsigned state) const { return m_delta[state].size(); } + move const& get_move_from(unsigned state) const { SASSERT(m_delta[state].size() == 1); return m_delta[state][0]; } + move const& get_move_to(unsigned state) const { SASSERT(m_delta_inv[state].size() == 1); return m_delta_inv[state][0]; } + moves const& get_moves_from(unsigned state) const { return m_delta[state]; } + moves const& get_moves_to(unsigned state) const { return m_delta_inv[state]; } + bool initial_state_is_source() const { return m_delta_inv[m_init].empty(); } + bool is_final_state(unsigned s) const { return m_final_set.contains(s); } + bool is_epsilon_free() const { return m_is_epsilon_free; } + bool is_deterministic() const { return m_is_deterministic; } + bool is_empty() const { return m_final_states.empty(); } + unsigned final_state() const { return m_final_states[0]; } + bool has_single_final_sink() const { return m_final_states.size() == 1 && m_delta[final_state()].empty(); } + unsigned num_states() const { return m_delta.size(); } + bool is_loop_state(unsigned s) const { + moves mvs; + get_moves_from(s, mvs); + for (unsigned i = 0; i < mvs.size(); ++i) { + if (s == mvs[i].dst()) return true; + } + return false; + } + + unsigned move_count() const { + unsigned result = 0; + for (unsigned i = 0; i < m_delta.size(); result += m_delta[i].size(), ++i) {} + return result; + } + void get_epsilon_closure(unsigned state, unsigned_vector& states) { + get_epsilon_closure(state, m_delta, states); + } + void get_inv_epsilon_closure(unsigned state, unsigned_vector& states) { + get_epsilon_closure(state, m_delta_inv, states); + } + void get_moves_from(unsigned state, moves& mvs) const { + get_moves(state, m_delta, mvs); + } + void get_moves_to(unsigned state, moves& mvs) { + get_moves(state, m_delta_inv, mvs); + } +private: + void get_moves(unsigned state, svector const& delta, moves& mvs) const { + unsigned_vector states; + get_epsilon_closure(state, delta, states); + for (unsigned i = 0; i < states.size(); ++i) { + state = states[i]; + moves const& mv1 = delta[state]; + for (unsigned j = 0; j < mv1.size(); ++j) { + if (!mv1[j].is_epsilon()) { + mvs.push_back(mv1[j]); + } + } + } + } + + void get_epsilon_closure(unsigned state, svector const& delta, unsigned_vector& states) const { + m_todo.push_back(state); + m_visited.insert(state); + while (!m_todo.empty()) { + state = m_todo.back(); + states.push_back(state); + m_todo.pop_back(); + moves const& mvs = delta[state]; + for (unsigned i = 0; i < mvs.size(); ++i) { + unsigned tgt = mvs[i].dst(); + if (mvs[i].is_epsilon() && !m_visited.contains(tgt)) { + m_visited.insert(tgt); + m_todo.push_back(tgt); + } + } + } + m_visited.reset(); + SASSERT(m_todo.empty()); + } + + static void append_moves(unsigned offset, automaton const& a, moves& mvs) { + for (unsigned i = 0; i < a.num_states(); ++i) { + moves const& mvs1 = a.m_delta[i]; + for (unsigned j = 0; j < mvs1.size(); ++j) { + move const& mv = mvs1[j]; + mvs.push_back(move(mv.src() + offset, mv.dst() + offset, mv.t())); + } + } + } + + static void append_final(unsigned offset, automaton const& a, unsigned_vector& final) { + for (unsigned i = 0; i < a.m_final_states.size(); ++i) { + final.push_back(a.m_final_states[i]+offset); + } + } + +}; + +typedef automaton uautomaton; + + +#endif diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 26ca2e68a..738763c88 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1382,7 +1382,8 @@ namespace smt { bool_var v = l.var(); bool_var_data & d = get_bdata(v); lbool val = get_assignment(v); - TRACE("propagate_atoms", tout << "propagating atom, #" << bool_var2expr(v)->get_id() << ", is_enode(): " << d.is_enode() << " " << l << "\n";); + TRACE("propagate_atoms", tout << "propagating atom, #" << bool_var2expr(v)->get_id() << ", is_enode(): " << d.is_enode() + << " tag: " << (d.is_eq()?"eq":"") << (d.is_theory_atom()?"th":"") << (d.is_quantifier()?"q":"") << " " << l << "\n";); SASSERT(val != l_undef); if (d.is_enode()) propagate_bool_var_enode(v); @@ -1404,6 +1405,7 @@ namespace smt { else if (d.is_theory_atom()) { theory * th = m_theories.get_plugin(d.get_theory()); SASSERT(th); + TRACE("seq", tout << d.get_theory() << "\n";); th->assign_eh(v, val == l_true); } else if (d.is_quantifier()) { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 79779d088..1cae448b2 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -125,6 +125,7 @@ theory_seq::theory_seq(ast_manager& m): m_rep(m, m_dm), m_factory(0), m_ineqs(m), + m_in_re(m), m_exclude(m), m_axioms(m), m_axioms_head(0), @@ -379,6 +380,9 @@ bool theory_seq::is_solved() { if (!check_ineq_coherence()) { return false; } + if (!m_in_re.empty()) { + return false; + } SASSERT(check_length_coherence()); @@ -572,7 +576,6 @@ bool theory_seq::solve_nqs() { bool change = false; context & ctx = get_context(); for (unsigned i = 0; !ctx.inconsistent() && i < m_nqs.size(); ++i) { - TRACE("seq", tout << i << " " << m_nqs.size() << "\n";); if (!m_nqs[i].is_solved()) { change = solve_ne(i) || change; } @@ -637,6 +640,16 @@ bool theory_seq::solve_ne(unsigned idx) { literal lit(mk_eq(nl, nr, false)); m_trail_stack.push(push_lit(*this, idx, lit)); ctx.mark_as_relevant(lit); + switch (ctx.get_assignment(lit)) { + case l_false: + mark_solved(idx); + return false; + case l_true: + break; + case l_undef: + ++num_undef_lits; + break; + } } } m_trail_stack.push(push_dep(*this, idx, deps)); @@ -696,6 +709,8 @@ bool theory_seq::internalize_term(app* term) { if (m.is_bool(term)) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); + ctx.mark_as_relevant(bv); + TRACE("seq", tout << mk_pp(term, m) << ": " << bv << "\n";); } else { enode* e = 0; @@ -751,6 +766,12 @@ void theory_seq::display(std::ostream & out) const { out << mk_pp(m_ineqs[i], m) << "\n"; } } + if (!m_in_re.empty()) { + out << "Regex\n"; + for (unsigned i = 0; i < m_in_re.size(); ++i) { + out << mk_pp(m_in_re[i], m) << "\n"; + } + } if (!m_rep.empty()) { out << "Solved equations:\n"; m_rep.display(out); @@ -917,7 +938,12 @@ expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { enode* n = get_context().get_enode(e)->get_root(); result = n->get_owner(); if (!m.is_model_value(result)) { - result = m_mg->get_some_value(m.get_sort(result)); + if (m_util.is_char(result)) { + result = m_util.str.mk_char('#'); + } + else { + result = m_mg->get_some_value(m.get_sort(result)); + } } m_rep.update(e, result, 0); TRACE("seq", tout << mk_pp(e, m) << " |-> " << result << "\n";); @@ -1288,13 +1314,16 @@ expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { context& ctx = get_context(); - TRACE("seq", - tout << mk_pp(ctx.bool_var2enode(v)->get_owner(), m) << " => " - << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); SASSERT(ctx.e_internalized(e2)); enode* n1 = ensure_enode(e1); enode* n2 = ensure_enode(e2); + if (n1->get_root() == n2->get_root()) { + return; + } + TRACE("seq", + tout << mk_pp(ctx.bool_var2enode(v)->get_owner(), m) << " => " + << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); literal lit(v); justification* js = ctx.mk_justification( @@ -1304,10 +1333,9 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { ctx.assign_eq(n1, n2, eq_justification(js)); } -void theory_seq::assign_eq(bool_var v, bool is_true) { +void theory_seq::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); - enode* n = ctx.bool_var2enode(v); - app* e = n->get_owner(); + expr* e = ctx.bool_var2expr(v); if (is_true) { expr* e1, *e2; expr_ref f(m); @@ -1327,8 +1355,10 @@ void theory_seq::assign_eq(bool_var v, bool is_true) { f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2); propagate_eq(v, f, e1); } - else if (m_util.str.is_in_re(e, e1, e2)) { - // TBD + else if (m_util.str.is_in_re(e)) { + TRACE("seq", tout << "in re: " << mk_pp(e, m) << "\n";); + m_trail_stack.push(push_back_vector(m_in_re)); + m_in_re.push_back(e); } else { UNREACHABLE(); @@ -1403,4 +1433,22 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_string(n)) { enque_axiom(n); } + if (m_util.str.is_in_re(n) || + m_util.str.is_contains(n) || + m_util.str.is_suffix(n) || + m_util.str.is_prefix(n)) { + context& ctx = get_context(); + TRACE("seq", tout << mk_pp(n, m) << "\n";); + bool_var bv = ctx.get_bool_var(n); + switch (ctx.get_assignment(bv)) { + case l_false: + assign_eh(bv, false); + break; + case l_true: + assign_eh(bv, true); + break; + case l_undef: + break; + } + } } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 21aabb599..43852a086 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -251,6 +251,7 @@ namespace smt { seq_factory* m_factory; // value factory expr_ref_vector m_ineqs; // inequalities to check solution against + expr_ref_vector m_in_re; // regular expression membership exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. unsigned m_axioms_head; // index of first axiom to add. @@ -277,7 +278,7 @@ namespace smt { virtual void internalize_eq_eh(app * atom, bool_var v); virtual void new_eq_eh(theory_var, theory_var); virtual void new_diseq_eh(theory_var, theory_var); - virtual void assign_eq(bool_var v, bool is_true); + virtual void assign_eh(bool_var v, bool is_true); virtual bool can_propagate(); virtual void propagate(); virtual void push_scope_eh(); From 2a7f2ab7f8fbd060c2a8dd7dfb9e3ca11504596f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 23 Dec 2015 20:33:55 -0800 Subject: [PATCH 09/35] sequence automaton Signed-off-by: Nikolaj Bjorner --- src/math/automata/automaton.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index e28db8fa0..7a224b5d4 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -94,9 +94,25 @@ public: } } + // create an automaton that accepts a sequence. + automaton(ptr_vector const& seq): + m_init(0), + m_is_epsilon_free(true), + m_is_deterministic(true) { + m_delta.resize(seq.size()+1, moves()); + m_delta_inv.resize(seq.size()+1, moves()); + for (unsigned i = 0; i < seq.size(); ++i) { + m_delta[i].push_back(move(i, i + 1, seq[i])); + m_delta[i + 1].push_back(move(i, i + 1, seq[i])); + } + m_final_states.push_back(seq.size()); + m_final_set.insert(seq.size()); + } + // The automaton with a single state that is also final. - automaton(T* t, unsigned s = 0): - m_init(s) { + automaton(T* t): + m_init(0) { + unsigned s = 0; m_delta.resize(s+1, moves()); m_delta_inv.resize(s+1, moves()); m_final_set.insert(s); From 1bbf7813b04be6621b9f27c89d27d36c98ffd2da Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Dec 2015 03:30:02 -0800 Subject: [PATCH 10/35] automata Signed-off-by: Nikolaj Bjorner --- src/math/automata/automaton.h | 186 +++++++++++++++++++++++++++------- src/smt/theory_seq.cpp | 91 ++++++++++++++++- src/smt/theory_seq.h | 12 +++ 3 files changed, 250 insertions(+), 39 deletions(-) diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index 7a224b5d4..cc9fe2c02 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -27,13 +27,42 @@ Revision History: #include "uint_set.h" template +class default_value_manager { +public: + void inc_ref(T* t) {} + void dec_ref(T* t) {} +}; + +template > class automaton { +public: class move { + M& m; T* m_t; unsigned m_src; unsigned m_dst; public: - move(unsigned s, unsigned d, T* t = 0): m_t(t), m_src(s), m_dst(d) {} + move(M& m, unsigned s, unsigned d, T* t = 0): m(m), m_t(t), m_src(s), m_dst(d) { + if (t) m.inc_ref(t); + } + ~move() { + if (m_t) m.dec_ref(m_t); + } + + move(move const& other): m(other.m), m_t(other.m_t), m_src(other.m_src), m_dst(other.m_dst) { + if (m_t) m.inc_ref(m_t); + } + + move& operator=(move const& other) { + SASSERT(&m == &other.m); + T* t = other.m_t; + if (t) m.inc_ref(t); + if (m_t) m.dec_ref(m_t); + m_t = t; + m_src = other.m_src; + m_dst = other.m_dst; + return *this; + } unsigned dst() const { return m_dst; } unsigned src() const { return m_src; } @@ -41,11 +70,11 @@ class automaton { bool is_epsilon() const { return m_t == 0; } }; - typedef svector moves; - - - svector m_delta; - svector m_delta_inv; + typedef vector moves; +private: + M& m; + vector m_delta; + vector m_delta_inv; unsigned m_init; uint_set m_final_set; unsigned_vector m_final_states; @@ -57,11 +86,11 @@ class automaton { mutable uint_set m_visited; mutable unsigned_vector m_todo; - public: // The empty automaton: - automaton(): + automaton(M& m): + m(m), m_init(0), m_is_epsilon_free(true), m_is_deterministic(true) @@ -72,7 +101,7 @@ public: // create an automaton from initial state, final states, and moves - automaton(unsigned init, unsigned_vector const& final, moves const& mvs) { + automaton(M& m, unsigned init, unsigned_vector const& final, moves const& mvs): m(m) { m_is_epsilon_free = true; m_is_deterministic = true; m_init = init; @@ -95,33 +124,35 @@ public: } // create an automaton that accepts a sequence. - automaton(ptr_vector const& seq): + automaton(M& m, ptr_vector const& seq): + m(m), m_init(0), m_is_epsilon_free(true), m_is_deterministic(true) { m_delta.resize(seq.size()+1, moves()); m_delta_inv.resize(seq.size()+1, moves()); for (unsigned i = 0; i < seq.size(); ++i) { - m_delta[i].push_back(move(i, i + 1, seq[i])); - m_delta[i + 1].push_back(move(i, i + 1, seq[i])); + m_delta[i].push_back(move(m, i, i + 1, seq[i])); + m_delta[i + 1].push_back(move(m, i, i + 1, seq[i])); } m_final_states.push_back(seq.size()); m_final_set.insert(seq.size()); } - // The automaton with a single state that is also final. - automaton(T* t): + // The automaton that accepts t + automaton(M& m, T* t): + m(m), m_init(0) { - unsigned s = 0; - m_delta.resize(s+1, moves()); - m_delta_inv.resize(s+1, moves()); - m_final_set.insert(s); - m_final_states.push_back(s); - m_delta[s].push_back(move(s, s, t)); - m_delta_inv[s].push_back(move(s, s, t)); + m_delta.resize(2, moves()); + m_delta_inv.resize(2, moves()); + m_final_set.insert(1); + m_final_states.push_back(1); + m_delta[0].push_back(move(m, 0, 1, t)); + m_delta_inv[1].push_back(move(m, 0, 1, t)); } automaton(automaton const& other): + m(other.m), m_delta(other.m_delta), m_delta_inv(other.m_delta_inv), m_init(other.m_init), @@ -132,47 +163,78 @@ public: {} // create the automaton that accepts the empty string only. - static automaton mk_epsilon() { + static automaton* mk_epsilon(M& m) { moves mvs; unsigned_vector final; final.push_back(0); - return automaton(0, final, mvs); + return alloc(automaton, m, 0, final, mvs); } // create the automaton with a single state on condition t. - static automaton mk_loop(T* t) { + static automaton* mk_loop(M& m, T* t) { moves mvs; unsigned_vector final; final.push_back(0); - mvs.push_back(move(0, 0, t)); - return automaton(0, final, mvs); + mvs.push_back(move(m, 0, 0, t)); + return alloc(automaton, m, 0, final, mvs); } // create the sum of disjoint automata - static automaton mk_union(automaton const& a, automaton const& b) { + static automaton* mk_union(automaton const& a, automaton const& b) { + SASSERT(&a.m == &b.m); + M& m = a.m; moves mvs; unsigned_vector final; unsigned offset1 = 1; unsigned offset2 = a.num_states() + 1; - mvs.push_back(move(0, a.init() + offset1, 0)); - mvs.push_back(move(0, b.init() + offset2, 0)); + mvs.push_back(move(m, 0, a.init() + offset1, 0)); + mvs.push_back(move(m, 0, b.init() + offset2, 0)); append_moves(offset1, a, mvs); append_moves(offset2, b, mvs); append_final(offset1, a, final); append_final(offset2, b, final); - return automaton(0, final, mvs); + return alloc(automaton, m, 0, final, mvs); } - static automaton mk_reverse(automaton const& a) { + // concatenate accepting languages + static automaton* mk_concat(automaton const& a, automaton const& b) { + SASSERT(&a.m == &b.m); + M& m = a.m; + moves mvs; + unsigned_vector final; + unsigned init = 0; + if (a.has_single_final_sink() && b.initial_state_is_source() && b.init() == 0) { + unsigned offset2 = a.num_states(); + init = a.init(); + append_moves(0, a, mvs); + append_moves(offset2, b, mvs); + append_final(offset2, b, final); + } + else { + unsigned offset1 = 1; + unsigned offset2 = a.num_states() + offset1; + mvs.push_back(move(m, 0, a.init() + offset1)); + append_moves(offset1, a, mvs); + for (unsigned i = 0; i < a.m_final_states.size(); ++i) { + mvs.push_back(move(m, a.m_final_states[i], b.init())); + } + append_moves(offset2, b, mvs); + append_final(offset2, b, final); + } + return alloc(automaton, m, init, final, mvs); + } + + static automaton* mk_reverse(automaton const& a) { + M& m = a.m; if (a.is_empty()) { - return automaton(); + return alloc(automaton, m); } moves mvs; for (unsigned i = 0; i < a.m_delta.size(); ++i) { moves const& mvs1 = a.m_delta[i]; for (unsigned j = 0; j < mvs1.size(); ++j) { move const& mv = mvs1[j]; - mvs.push_back(move(mv.dst(), mv.src(), mv.t())); + mvs.push_back(move(m, mv.dst(), mv.src(), mv.t())); } } unsigned_vector final; @@ -184,10 +246,37 @@ public: else { init = a.num_states(); for (unsigned i = 0; i < a.m_final_states.size(); ++i) { - mvs.push_back(move(init, a.m_final_states[i])); + mvs.push_back(move(m, init, a.m_final_states[i])); } } - return automaton(init, final, mvs); + return alloc(automaton, m, init, final, mvs); + } + + void add_init_to_final() { + if (!m_final_set.contains(m_init)) { + m_final_set.insert(m_init); + m_final_states.push_back(m_init); + } + } + + void add_final_to_init_moves() { + for (unsigned i = 0; i < m_final_states.size(); ++i) { + unsigned state = m_final_states[i]; + bool found = false; + moves const& mvs = m_delta[state]; + for (unsigned j = 0; found && j < mvs.size(); ++j) { + found = (mvs[j].dst() == m_init) && mvs[j].is_epsilon(); + } + if (!found) { + m_delta[state].push_back(move(m, state, m_init)); + m_delta_inv[m_init].push_back(move(m, state, m_init)); + } + } + } + + // remove states that only have epsilon transitions. + void compress() { + // TBD } bool is_sequence(unsigned& length) const { @@ -252,8 +341,29 @@ public: void get_moves_to(unsigned state, moves& mvs) { get_moves(state, m_delta_inv, mvs); } + + template + std::ostream& display(std::ostream& out, D& displayer) const { + out << "init: " << init() << "\n"; + out << "final: "; + for (unsigned i = 0; i < m_final_states.size(); ++i) out << m_final_states[i] << " "; + out << "\n"; + for (unsigned i = 0; i < m_delta.size(); ++i) { + moves const& mvs = m_delta[i]; + for (unsigned j = 0; j < mvs.size(); ++j) { + move const& mv = mvs[j]; + out << i << " -> " << mv.dst() << " "; + if (mv.t()) { + out << "if "; + displayer.display(out, mv.t()); + } + out << "\n"; + } + } + return out; + } private: - void get_moves(unsigned state, svector const& delta, moves& mvs) const { + void get_moves(unsigned state, vector const& delta, moves& mvs) const { unsigned_vector states; get_epsilon_closure(state, delta, states); for (unsigned i = 0; i < states.size(); ++i) { @@ -267,7 +377,7 @@ private: } } - void get_epsilon_closure(unsigned state, svector const& delta, unsigned_vector& states) const { + void get_epsilon_closure(unsigned state, vector const& delta, unsigned_vector& states) const { m_todo.push_back(state); m_visited.insert(state); while (!m_todo.empty()) { @@ -292,7 +402,7 @@ private: moves const& mvs1 = a.m_delta[i]; for (unsigned j = 0; j < mvs1.size(); ++j) { move const& mv = mvs1[j]; - mvs.push_back(move(mv.src() + offset, mv.dst() + offset, mv.t())); + mvs.push_back(move(a.m, mv.src() + offset, mv.dst() + offset, mv.t())); } } } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 1cae448b2..b729ddebe 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -26,6 +26,81 @@ Revision History: using namespace smt; + +re2automaton::re2automaton(ast_manager& m): m(m), u(m) {} + +eautomaton* re2automaton::re2aut(expr* e) { + SASSERT(u.is_re(e)); + expr* e1, *e2; + scoped_ptr a, b; + if (u.re.is_to_re(e, e1)) { + return seq2aut(e1); + } + else if (u.re.is_concat(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { + return eautomaton::mk_concat(*a, *b); + } + else if (u.re.is_union(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { + return eautomaton::mk_union(*a, *b); + } + else if (u.re.is_star(e, e1) && (a = re2aut(e1))) { + a->add_final_to_init_moves(); + a->add_init_to_final(); + return a.detach(); + } + else if (u.re.is_plus(e, e1) && (a = re2aut(e1))) { + a->add_final_to_init_moves(); + return a.detach(); + } + else if (u.re.is_opt(e, e1) && (a = re2aut(e1))) { + a->add_init_to_final(); + return a.detach(); + } + else if (u.re.is_range(e)) { + + } + else if (u.re.is_loop(e)) { + + } +#if 0 + else if (u.re.is_intersect(e, e1, e2)) { + + } + else if (u.re.is_empty(e)) { + + } +#endif + + return 0; +} + +eautomaton* re2automaton::seq2aut(expr* e) { + SASSERT(u.is_seq(e)); + zstring s; + expr* e1, *e2; + scoped_ptr a, b; + if (u.str.is_concat(e, e1, e2) && (a = seq2aut(e1)) && (b = seq2aut(e2))) { + return eautomaton::mk_concat(*a, *b); + } + else if (u.str.is_unit(e, e1)) { + return alloc(eautomaton, m, e1); + } + else if (u.str.is_empty(e)) { + return eautomaton::mk_epsilon(m); + } + else if (u.str.is_string(e, s)) { + unsigned init = 0; + eautomaton::moves mvs; + unsigned_vector final; + final.push_back(s.length()); + for (unsigned k = 0; k < s.length(); ++k) { + // reference count? + mvs.push_back(eautomaton::move(m, k, k+1, u.str.mk_char(s, k))); + } + return alloc(eautomaton, m, init, final, mvs); + } + return 0; +} + void theory_seq::solution_map::update(expr* e, expr* r, enode_pair_dependency* d) { m_cache.reset(); std::pair value; @@ -1333,6 +1408,14 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { ctx.assign_eq(n1, n2, eq_justification(js)); } +struct display_expr { + ast_manager& m; + display_expr(ast_manager& m): m(m) {} + std::ostream& display(std::ostream& out, expr* e) const { + return out << mk_pp(e, m); + } +}; + void theory_seq::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); expr* e = ctx.bool_var2expr(v); @@ -1355,10 +1438,16 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2); propagate_eq(v, f, e1); } - else if (m_util.str.is_in_re(e)) { + else if (m_util.str.is_in_re(e, e1, e2)) { TRACE("seq", tout << "in re: " << mk_pp(e, m) << "\n";); m_trail_stack.push(push_back_vector(m_in_re)); m_in_re.push_back(e); + + scoped_ptr a = re2automaton(m)(e2); + if (a) { + display_expr disp(m); + TRACE("seq", a->display(tout, disp);); + } } else { UNREACHABLE(); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 43852a086..23eac1c8b 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -25,9 +25,21 @@ Revision History: #include "th_rewriter.h" #include "ast_trail.h" #include "scoped_vector.h" +#include "automaton.h" namespace smt { + typedef automaton eautomaton; + class re2automaton { + ast_manager& m; + seq_util u; + eautomaton* re2aut(expr* e); + eautomaton* seq2aut(expr* e); + public: + re2automaton(ast_manager& m); + eautomaton* operator()(expr* e) { return re2aut(e); } + }; + class theory_seq : public theory { typedef scoped_dependency_manager enode_pair_dependency_manager; typedef enode_pair_dependency_manager::dependency enode_pair_dependency; From 65d147106e1ddcadc7b7772e1ec45c34b5a3eccf Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 24 Dec 2015 12:01:59 -0800 Subject: [PATCH 11/35] automata Signed-off-by: Nikolaj Bjorner --- src/math/automata/automaton.h | 59 ++++++++++----- src/smt/theory_seq.cpp | 136 +++++++++++++++++++++++++++------- src/smt/theory_seq.h | 15 +++- src/util/scoped_ptr_vector.h | 1 + 4 files changed, 163 insertions(+), 48 deletions(-) diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index cc9fe2c02..606991f6a 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -78,8 +78,6 @@ private: unsigned m_init; uint_set m_final_set; unsigned_vector m_final_states; - bool m_is_epsilon_free; - bool m_is_deterministic; // local data-structures @@ -91,9 +89,7 @@ public: // The empty automaton: automaton(M& m): m(m), - m_init(0), - m_is_epsilon_free(true), - m_is_deterministic(true) + m_init(0) { m_delta.push_back(moves()); m_delta_inv.push_back(moves()); @@ -102,8 +98,6 @@ public: // create an automaton from initial state, final states, and moves automaton(M& m, unsigned init, unsigned_vector const& final, moves const& mvs): m(m) { - m_is_epsilon_free = true; - m_is_deterministic = true; m_init = init; for (unsigned i = 0; i < final.size(); ++i) { m_final_states.push_back(final[i]); @@ -118,17 +112,13 @@ public: } m_delta[mv.src()].push_back(mv); m_delta_inv[mv.dst()].push_back(mv); - m_is_deterministic &= m_delta[mv.src()].size() < 2; - m_is_epsilon_free &= !mv.is_epsilon(); } } // create an automaton that accepts a sequence. automaton(M& m, ptr_vector const& seq): m(m), - m_init(0), - m_is_epsilon_free(true), - m_is_deterministic(true) { + m_init(0) { m_delta.resize(seq.size()+1, moves()); m_delta_inv.resize(seq.size()+1, moves()); for (unsigned i = 0; i < seq.size(); ++i) { @@ -157,9 +147,7 @@ public: m_delta_inv(other.m_delta_inv), m_init(other.m_init), m_final_set(other.m_final_set), - m_final_states(other.m_final_states), - m_is_epsilon_free(other.m_is_epsilon_free), - m_is_deterministic(other.m_is_deterministic) + m_final_states(other.m_final_states) {} // create the automaton that accepts the empty string only. @@ -187,8 +175,8 @@ public: unsigned_vector final; unsigned offset1 = 1; unsigned offset2 = a.num_states() + 1; - mvs.push_back(move(m, 0, a.init() + offset1, 0)); - mvs.push_back(move(m, 0, b.init() + offset2, 0)); + mvs.push_back(move(m, 0, a.init() + offset1)); + mvs.push_back(move(m, 0, b.init() + offset2)); append_moves(offset1, a, mvs); append_moves(offset2, b, mvs); append_final(offset1, a, final); @@ -196,6 +184,23 @@ public: return alloc(automaton, m, 0, final, mvs); } + static automaton* mk_opt(automaton const& a) { + M& m = a.m; + moves mvs; + unsigned_vector final; + unsigned offset = 0; + unsigned init = a.init(); + if (!a.initial_state_is_source()) { + offset = 1; + init = 0; + mvs.push_back(move(m, 0, a.init() + offset)); + } + mvs.push_back(move(m, init, a.final_state() + offset)); + append_moves(offset, a, mvs); + append_final(offset, a, final); + return alloc(automaton, m, init, final, mvs); + } + // concatenate accepting languages static automaton* mk_concat(automaton const& a, automaton const& b) { SASSERT(&a.m == &b.m); @@ -276,6 +281,7 @@ public: // remove states that only have epsilon transitions. void compress() { + // TBD } @@ -309,8 +315,23 @@ public: moves const& get_moves_to(unsigned state) const { return m_delta_inv[state]; } bool initial_state_is_source() const { return m_delta_inv[m_init].empty(); } bool is_final_state(unsigned s) const { return m_final_set.contains(s); } - bool is_epsilon_free() const { return m_is_epsilon_free; } - bool is_deterministic() const { return m_is_deterministic; } + bool is_epsilon_free() const { + for (unsigned i = 0; i < m_delta.size(); ++i) { + moves const& mvs = m_delta[i]; + for (unsigned j = 0; j < mvs.size(); ++j) { + if (!mvs[j].t()) return false; + } + } + return true; + } + + bool is_deterministic() const { + for (unsigned i = 0; i < m_delta.size(); ++i) { + if (m_delta[i].size() >= 2) return false; + } + return true; + } + bool is_empty() const { return m_final_states.empty(); } unsigned final_state() const { return m_final_states[0]; } bool has_single_final_sink() const { return m_final_states.size() == 1 && m_delta[final_state()].empty(); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index b729ddebe..a379d9eed 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -26,6 +26,14 @@ Revision History: using namespace smt; +struct display_expr { + ast_manager& m; + display_expr(ast_manager& m): m(m) {} + std::ostream& display(std::ostream& out, expr* e) const { + return out << mk_pp(e, m); + } +}; + re2automaton::re2automaton(ast_manager& m): m(m), u(m) {} @@ -52,7 +60,7 @@ eautomaton* re2automaton::re2aut(expr* e) { return a.detach(); } else if (u.re.is_opt(e, e1) && (a = re2aut(e1))) { - a->add_init_to_final(); + a = eautomaton::mk_opt(*a); return a.detach(); } else if (u.re.is_range(e)) { @@ -200,7 +208,6 @@ theory_seq::theory_seq(ast_manager& m): m_rep(m, m_dm), m_factory(0), m_ineqs(m), - m_in_re(m), m_exclude(m), m_axioms(m), m_axioms_head(0), @@ -219,6 +226,7 @@ theory_seq::theory_seq(ast_manager& m): m_right_sym = "seq.right"; m_contains_left_sym = "seq.contains.left"; m_contains_right_sym = "seq.contains.right"; + m_accept_sym = "aut.accept"; } theory_seq::~theory_seq() { @@ -455,7 +463,7 @@ bool theory_seq::is_solved() { if (!check_ineq_coherence()) { return false; } - if (!m_in_re.empty()) { + if (!m_re2aut.empty()) { return false; } @@ -595,17 +603,15 @@ bool theory_seq::is_var(expr* a) { } bool theory_seq::is_left_select(expr* a, expr*& b) { - return m_util.is_skolem(a) && - to_app(a)->get_decl()->get_parameter(0).get_symbol() == m_left_sym && (b = to_app(a)->get_arg(0), true); + return is_skolem(m_left_sym, a) && (b = to_app(a)->get_arg(0), true); } bool theory_seq::is_right_select(expr* a, expr*& b) { - return m_util.is_skolem(a) && - to_app(a)->get_decl()->get_parameter(0).get_symbol() == m_right_sym && (b = to_app(a)->get_arg(0), true); + return is_skolem(m_right_sym, a) && (b = to_app(a)->get_arg(0), true); } bool theory_seq::is_head_elem(expr* e) const { - return m_util.is_skolem(e) && to_app(e)->get_decl()->get_parameter(0).get_symbol() == symbol("seq.head.elem"); + return is_skolem(symbol("seq.head.elem"), e); } void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { @@ -841,10 +847,13 @@ void theory_seq::display(std::ostream & out) const { out << mk_pp(m_ineqs[i], m) << "\n"; } } - if (!m_in_re.empty()) { + if (!m_re2aut.empty()) { out << "Regex\n"; - for (unsigned i = 0; i < m_in_re.size(); ++i) { - out << mk_pp(m_in_re[i], m) << "\n"; + obj_map::iterator it = m_re2aut.begin(), end = m_re2aut.end(); + for (; it != end; ++it) { + out << mk_pp(it->m_key, m) << "\n"; + display_expr disp(m); + it->m_value->display(out, disp); } } if (!m_rep.empty()) { @@ -1089,6 +1098,9 @@ void theory_seq::deque_axiom(expr* n) { add_elim_string_axiom(n); // add_length_string_axiom(n); } + else if (m_util.str.is_in_re(n)) { + add_in_re_axiom(n); + } } @@ -1281,6 +1293,76 @@ void theory_seq::add_length_axiom(expr* n) { } } +// the empty sequence is accepted only in the final states. +// membership holds iff the initial state holds. +void theory_seq::add_in_re_axiom(expr* n) { + expr* e1, *e2; + context& ctx = get_context(); + VERIFY(m_util.str.is_in_re(n, e1, e2)); + eautomaton* a = get_automaton(e2); + if (!a) return; + + expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); + for (unsigned i = 0; i < a->num_states(); ++i) { + expr_ref acc = mk_accept(emp, e2, m_autil.mk_int(i)); + literal lit = mk_literal(acc); + add_axiom(a->is_final_state(i)?lit:~lit); + } + unsigned_vector states; + a->get_epsilon_closure(a->init(), states); + literal_vector lits; + literal lit = mk_literal(n); + ctx.mark_as_relevant(lit); + lits.push_back(~lit); + for (unsigned i = 0; i < states.size(); ++i) { + expr_ref acc = mk_accept(e1, e2, m_autil.mk_int(a->init())); + literal lit2 = mk_literal(acc); + lits.push_back(lit2); + add_axiom(~lit2, lit); + } + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); +} + +eautomaton* theory_seq::get_automaton(expr* re) { + eautomaton* result = 0; + if (m_re2aut.find(re, result)) { + return result; + } + result = re2automaton(m)(re); + if (result) { + display_expr disp(m); + TRACE("seq", result->display(tout, disp);); + } + if (result) { + m_automata.push_back(result); + m_trail_stack.push(push_back_vector >(m_automata)); + } + m_re2aut.insert(re, result); + m_trail_stack.push(insert_obj_map(m_re2aut, re)); + return result; +} + +expr_ref theory_seq::mk_accept(expr* s, expr* re, expr* state) { + return expr_ref(mk_skolem(m_accept_sym, s, re, state, m.mk_bool_sort()), m); +} + + +bool theory_seq::is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { + if (is_accept(acc)) { + rational r; + s = to_app(acc)->get_arg(0); + re = to_app(acc)->get_arg(1); + VERIFY(m_autil.is_numeral(to_app(acc)->get_arg(2), r)); + SASSERT(r.is_unsigned()); + i = r.get_unsigned(); + aut = m_re2aut[re]; + return true; + } + else { + return false; + } +} + expr_ref theory_seq::mk_sub(expr* a, expr* b) { expr_ref result(m_autil.mk_sub(a, b), m); m_rewrite(result); @@ -1387,6 +1469,11 @@ expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, return expr_ref(m_util.mk_skolem(name, len, es, range), m); } +bool theory_seq::is_skolem(symbol const& s, expr* e) const { + return m_util.is_skolem(e) && to_app(e)->get_decl()->get_parameter(0).get_symbol() == s; +} + + void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { context& ctx = get_context(); @@ -1408,18 +1495,14 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { ctx.assign_eq(n1, n2, eq_justification(js)); } -struct display_expr { - ast_manager& m; - display_expr(ast_manager& m): m(m) {} - std::ostream& display(std::ostream& out, expr* e) const { - return out << mk_pp(e, m); - } -}; void theory_seq::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); expr* e = ctx.bool_var2expr(v); - if (is_true) { + if (is_accept(e)) { + // TBD + } + else if (is_true) { expr* e1, *e2; expr_ref f(m); if (m_util.str.is_prefix(e, e1, e2)) { @@ -1439,15 +1522,11 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { propagate_eq(v, f, e1); } else if (m_util.str.is_in_re(e, e1, e2)) { - TRACE("seq", tout << "in re: " << mk_pp(e, m) << "\n";); - m_trail_stack.push(push_back_vector(m_in_re)); - m_in_re.push_back(e); - scoped_ptr a = re2automaton(m)(e2); - if (a) { - display_expr disp(m); - TRACE("seq", a->display(tout, disp);); - } + // Predicate: accept(e1, e2, state) + // seq.in.re(e1,e2) <-> accept(e1, e2, init) + // accept("", e2, state) <-> state is final in a + // e = head.elem(e)*tail(e) -> accept(e, e2, state) <-> \/ accept(tail(e), e2, state') & label(t) = head.elem(e) for t : state->state' in a } else { UNREACHABLE(); @@ -1519,7 +1598,8 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_concat(n) || m_util.str.is_empty(n) || m_util.str.is_unit(n) || - m_util.str.is_string(n)) { + m_util.str.is_string(n) || + m_util.str.is_in_re(n)) { enque_axiom(n); } if (m_util.str.is_in_re(n) || diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 23eac1c8b..ab0038933 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -25,6 +25,7 @@ Revision History: #include "th_rewriter.h" #include "ast_trail.h" #include "scoped_vector.h" +#include "scoped_ptr_vector.h" #include "automaton.h" namespace smt { @@ -263,7 +264,6 @@ namespace smt { seq_factory* m_factory; // value factory expr_ref_vector m_ineqs; // inequalities to check solution against - expr_ref_vector m_in_re; // regular expression membership exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. unsigned m_axioms_head; // index of first axiom to add. @@ -283,6 +283,11 @@ namespace smt { symbol m_contains_right_sym; symbol m_left_sym; // split variable left part symbol m_right_sym; // split variable right part + symbol m_accept_sym; + + // maintain automata with regular expressions. + scoped_ptr_vector m_automata; + obj_map m_re2aut; virtual final_check_status final_check_eh(); virtual bool internalize_atom(app*, bool); @@ -361,15 +366,23 @@ namespace smt { void add_length_string_axiom(expr* n); void add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); + void add_in_re_axiom(expr* n); literal mk_literal(expr* n); void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal); expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); + bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); + // automata utilities + eautomaton* get_automaton(expr* e); + expr_ref mk_accept(expr* s, expr* re, expr* state); + bool is_accept(expr* acc) const { return is_skolem(m_accept_sym, acc); } + bool is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut); + // diagnostics void display_equations(std::ostream& out) const; void display_disequations(std::ostream& out) const; diff --git a/src/util/scoped_ptr_vector.h b/src/util/scoped_ptr_vector.h index 6e1c26f7d..d1ff89733 100644 --- a/src/util/scoped_ptr_vector.h +++ b/src/util/scoped_ptr_vector.h @@ -30,6 +30,7 @@ public: ~scoped_ptr_vector() { reset(); } void reset() { std::for_each(m_vector.begin(), m_vector.end(), delete_proc()); m_vector.reset(); } void push_back(T * ptr) { m_vector.push_back(ptr); } + void pop_back() { SASSERT(!empty()); set(size()-1, 0); m_vector.pop_back(); } T * operator[](unsigned idx) const { return m_vector[idx]; } void set(unsigned idx, T * ptr) { if (m_vector[idx] == ptr) From 659a7ede84454e3da91675e376bd8b2442f9c061 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Dec 2015 04:25:23 -0800 Subject: [PATCH 12/35] automata Signed-off-by: Nikolaj Bjorner --- src/math/automata/automaton.h | 20 +- src/smt/theory_seq.cpp | 391 ++++++++++++++++++++++++++-------- src/smt/theory_seq.h | 26 ++- 3 files changed, 338 insertions(+), 99 deletions(-) diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index 606991f6a..684082170 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -384,15 +384,23 @@ public: return out; } private: + mutable unsigned_vector m_states1, m_states2; + void get_moves(unsigned state, vector const& delta, moves& mvs) const { - unsigned_vector states; - get_epsilon_closure(state, delta, states); - for (unsigned i = 0; i < states.size(); ++i) { - state = states[i]; + m_states1.reset(); + m_states2.reset(); + get_epsilon_closure(state, delta, m_states1); + for (unsigned i = 0; i < m_states1.size(); ++i) { + state = m_states1[i]; moves const& mv1 = delta[state]; for (unsigned j = 0; j < mv1.size(); ++j) { - if (!mv1[j].is_epsilon()) { - mvs.push_back(mv1[j]); + move const& mv = mv1[j]; + if (!mv.is_epsilon()) { + m_states2.reset(); + get_epsilon_closure(mv.dst(), delta, m_states2); + for (unsigned k = 0; k < m_states2.size(); ++k) { + mvs.push_back(move(m, mv.src(), m_states2[k], mv.t())); + } } } } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index a379d9eed..ee2b3070e 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -219,7 +219,10 @@ theory_seq::theory_seq(ast_manager& m): m_rewrite(m), m_util(m), m_autil(m), - m_trail_stack(*this) { + m_trail_stack(*this), + m_accepts_qhead(0), + m_rejects_qhead(0), + m_steps_qhead(0) { m_prefix_sym = "seq.prefix.suffix"; m_suffix_sym = "seq.suffix.prefix"; m_left_sym = "seq.left"; @@ -249,6 +252,9 @@ final_check_status theory_seq::final_check_eh() { if (ctx.inconsistent()) { return FC_CONTINUE; } + if (propagate_automata()) { + return FC_CONTINUE; + } if (branch_variable()) { TRACE("seq", tout << "branch\n";); return FC_CONTINUE; @@ -416,19 +422,14 @@ bool theory_seq::check_length_coherence_tbd() { if (is_var(f) && f == e) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); TRACE("seq", tout << "Unsolved " << mk_pp(e, m) << "\n";); -#if 1 if (!assume_equality(e, emp)) { + expr_ref head(m), tail(m); + mk_decompose(e, emp, head, tail); // e = emp \/ e = unit(head.elem(e))*tail(e) - sort* char_sort = 0; - VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); - expr_ref tail(mk_skolem(symbol("seq.tail"), e), m); - expr_ref v(mk_skolem(symbol("seq.head.elem"), e, 0, 0, char_sort), m); - expr_ref head(m_util.str.mk_unit(v), m); expr_ref conc(m_util.str.mk_concat(head, tail), m); add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); assume_equality(tail, emp); } -#endif m_branch_variable_head = j + 1; return false; } @@ -436,6 +437,15 @@ bool theory_seq::check_length_coherence_tbd() { return coherent; } +void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail) { + sort* char_sort = 0; + VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); + tail = mk_skolem(symbol("seq.tail"), e); + expr_ref v(mk_skolem(symbol("seq.head.elem"), e, 0, 0, char_sort), m); + head = m_util.str.mk_unit(v); + emp = m_util.str.mk_empty(m.get_sort(e)); +} + bool theory_seq::check_ineq_coherence() { bool all_false = true; for (unsigned i = 0; all_false && i < m_ineqs.size(); ++i) { @@ -473,7 +483,7 @@ bool theory_seq::is_solved() { } -void theory_seq::propagate_lit(enode_pair_dependency* dep, literal lit) { +void theory_seq::propagate_lit(enode_pair_dependency* dep, unsigned n, literal const* lits, literal lit) { context& ctx = get_context(); ctx.mark_as_relevant(lit); vector _eqs; @@ -483,7 +493,7 @@ void theory_seq::propagate_lit(enode_pair_dependency* dep, literal lit) { justification* js = ctx.mk_justification( ext_theory_propagation_justification( - get_id(), ctx.get_region(), 0, 0, _eqs.size(), _eqs.c_ptr(), lit)); + get_id(), ctx.get_region(), n, lits, _eqs.size(), _eqs.c_ptr(), lit)); ctx.assign(lit, js); } @@ -1293,11 +1303,12 @@ void theory_seq::add_length_axiom(expr* n) { } } +// // the empty sequence is accepted only in the final states. // membership holds iff the initial state holds. +// void theory_seq::add_in_re_axiom(expr* n) { expr* e1, *e2; - context& ctx = get_context(); VERIFY(m_util.str.is_in_re(n, e1, e2)); eautomaton* a = get_automaton(e2); if (!a) return; @@ -1305,64 +1316,52 @@ void theory_seq::add_in_re_axiom(expr* n) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); for (unsigned i = 0; i < a->num_states(); ++i) { expr_ref acc = mk_accept(emp, e2, m_autil.mk_int(i)); - literal lit = mk_literal(acc); - add_axiom(a->is_final_state(i)?lit:~lit); + expr_ref rej = mk_reject(emp, e2, m_autil.mk_int(i)); + literal alit = mk_literal(acc); + literal rlit = mk_literal(rej); + add_axiom(a->is_final_state(i)?alit:~alit); + add_axiom(a->is_final_state(i)?~rlit:rlit); } +} + + +void theory_seq::propagate_in_re(expr* n, bool is_true) { + expr* e1, *e2; + VERIFY(m_util.str.is_in_re(n, e1, e2)); + eautomaton* a = get_automaton(e2); + if (!a) return; + if (m_util.str.is_empty(e1)) return; + context& ctx = get_context(); unsigned_vector states; a->get_epsilon_closure(a->init(), states); literal_vector lits; - literal lit = mk_literal(n); - ctx.mark_as_relevant(lit); - lits.push_back(~lit); + literal lit = ctx.get_literal(n); + if (is_true) { + lits.push_back(~lit); + } for (unsigned i = 0; i < states.size(); ++i) { - expr_ref acc = mk_accept(e1, e2, m_autil.mk_int(a->init())); - literal lit2 = mk_literal(acc); - lits.push_back(lit2); - add_axiom(~lit2, lit); + if (is_true) { + expr_ref acc = mk_accept(e1, e2, m_autil.mk_int(a->init())); + lits.push_back(mk_literal(acc)); + } + else { + expr_ref rej = mk_reject(e1, e2, m_autil.mk_int(a->init())); + literal rlit = mk_literal(rej); + literal nlit = ~lit; + propagate_lit(0, 1, &nlit, rlit); + } } - ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); -} - -eautomaton* theory_seq::get_automaton(expr* re) { - eautomaton* result = 0; - if (m_re2aut.find(re, result)) { - return result; - } - result = re2automaton(m)(re); - if (result) { - display_expr disp(m); - TRACE("seq", result->display(tout, disp);); - } - if (result) { - m_automata.push_back(result); - m_trail_stack.push(push_back_vector >(m_automata)); - } - m_re2aut.insert(re, result); - m_trail_stack.push(insert_obj_map(m_re2aut, re)); - return result; -} - -expr_ref theory_seq::mk_accept(expr* s, expr* re, expr* state) { - return expr_ref(mk_skolem(m_accept_sym, s, re, state, m.mk_bool_sort()), m); -} - - -bool theory_seq::is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { - if (is_accept(acc)) { - rational r; - s = to_app(acc)->get_arg(0); - re = to_app(acc)->get_arg(1); - VERIFY(m_autil.is_numeral(to_app(acc)->get_arg(2), r)); - SASSERT(r.is_unsigned()); - i = r.get_unsigned(); - aut = m_re2aut[re]; - return true; - } - else { - return false; + if (is_true) { + if (lits.size() == 2) { + propagate_lit(0, 1, &lit, lits[1]); + } + else { + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + } } } + expr_ref theory_seq::mk_sub(expr* a, expr* b) { expr_ref result(m_autil.mk_sub(a, b), m); m_rewrite(result); @@ -1439,6 +1438,16 @@ void theory_seq::add_at_axiom(expr* e) { add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); } +/** + step(s, tail, re, i, j, t) -> s = t ++ tail +*/ +void theory_seq::propagate_step(bool_var v, expr* step) { + expr* re, *t, *s, *tail, *i, *j; + VERIFY(is_step(step, s, tail, re, i, j, t)); + expr_ref conc(m_util.str.mk_concat(m_util.str.mk_unit(t), tail), m); + propagate_eq(v, s, conc); +} + literal theory_seq::mk_literal(expr* _e) { expr_ref e(_e, m); @@ -1499,40 +1508,45 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { void theory_seq::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); expr* e = ctx.bool_var2expr(v); - if (is_accept(e)) { - // TBD + expr* e1, *e2; + expr_ref f(m); + + if (is_true && m_util.str.is_prefix(e, e1, e2)) { + f = mk_skolem(m_prefix_sym, e1, e2); + f = m_util.str.mk_concat(e1, f); + propagate_eq(v, f, e2); } - else if (is_true) { - expr* e1, *e2; - expr_ref f(m); - if (m_util.str.is_prefix(e, e1, e2)) { - f = mk_skolem(m_prefix_sym, e1, e2); - f = m_util.str.mk_concat(e1, f); - propagate_eq(v, f, e2); - } - else if (m_util.str.is_suffix(e, e1, e2)) { - f = mk_skolem(m_suffix_sym, e1, e2); - f = m_util.str.mk_concat(f, e1); - propagate_eq(v, f, e2); - } - else if (m_util.str.is_contains(e, e1, e2)) { - expr_ref f1 = mk_skolem(m_contains_left_sym, e1, e2); - expr_ref f2 = mk_skolem(m_contains_right_sym, e1, e2); - f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2); - propagate_eq(v, f, e1); - } - else if (m_util.str.is_in_re(e, e1, e2)) { - - // Predicate: accept(e1, e2, state) - // seq.in.re(e1,e2) <-> accept(e1, e2, init) - // accept("", e2, state) <-> state is final in a - // e = head.elem(e)*tail(e) -> accept(e, e2, state) <-> \/ accept(tail(e), e2, state') & label(t) = head.elem(e) for t : state->state' in a - } - else { - UNREACHABLE(); + else if (is_true && m_util.str.is_suffix(e, e1, e2)) { + f = mk_skolem(m_suffix_sym, e1, e2); + f = m_util.str.mk_concat(f, e1); + propagate_eq(v, f, e2); + } + else if (is_true && m_util.str.is_contains(e, e1, e2)) { + expr_ref f1 = mk_skolem(m_contains_left_sym, e1, e2); + expr_ref f2 = mk_skolem(m_contains_right_sym, e1, e2); + f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2); + propagate_eq(v, f, e1); + } + else if (is_accept(e)) { + m_trail_stack.push(push_back_vector >(m_accepts)); + m_accepts.push_back(e); + } + else if (is_reject(e)) { + m_trail_stack.push(push_back_vector >(m_rejects)); + m_rejects.push_back(e); + } + else if (is_step(e)) { + if (is_true) { + propagate_step(v, e); + m_trail_stack.push(push_back_vector >(m_steps)); + m_steps.push_back(e); } } + else if (m_util.str.is_in_re(e)) { + propagate_in_re(e, is_true); + } else { + SASSERT(!is_true); //if (m_util.str.is_prefix(e, e1, e2)) { // could add negative prefix axioms: // len(e1) <= len(e2) => e2 = seq.prefix.left(e2)*seq.prefix.right(e2) @@ -1599,9 +1613,11 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_empty(n) || m_util.str.is_unit(n) || m_util.str.is_string(n) || - m_util.str.is_in_re(n)) { + m_util.str.is_in_re(n) || + is_step(n)) { enque_axiom(n); } +#if 0 if (m_util.str.is_in_re(n) || m_util.str.is_contains(n) || m_util.str.is_suffix(n) || @@ -1620,4 +1636,197 @@ void theory_seq::relevant_eh(app* n) { break; } } +#endif +} + + +eautomaton* theory_seq::get_automaton(expr* re) { + eautomaton* result = 0; + if (m_re2aut.find(re, result)) { + return result; + } + result = re2automaton(m)(re); + if (result) { + display_expr disp(m); + TRACE("seq", result->display(tout, disp);); + } + if (result) { + m_automata.push_back(result); + m_trail_stack.push(push_back_vector >(m_automata)); + } + m_re2aut.insert(re, result); + m_trail_stack.push(insert_obj_map(m_re2aut, re)); + return result; +} + +expr_ref theory_seq::mk_accept(expr* s, expr* re, expr* state) { + return expr_ref(mk_skolem(m_accept_sym, s, re, state, m.mk_bool_sort()), m); +} +expr_ref theory_seq::mk_reject(expr* s, expr* re, expr* state) { + return expr_ref(mk_skolem(m_reject_sym, s, re, state, m.mk_bool_sort()), m); +} + +bool theory_seq::is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { + if (is_skolem(ar, e)) { + rational r; + s = to_app(e)->get_arg(0); + re = to_app(e)->get_arg(1); + VERIFY(m_autil.is_numeral(to_app(e)->get_arg(2), r)); + SASSERT(r.is_unsigned()); + i = r.get_unsigned(); + aut = m_re2aut[re]; + return true; + } + else { + return false; + } +} + +bool theory_seq::is_step(expr* e) const { + return is_skolem(symbol("aut.step"), e); +} + +bool theory_seq::is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const { + if (is_step(e)) { + s = to_app(e)->get_arg(0); + tail = to_app(e)->get_arg(1); + re = to_app(e)->get_arg(2); + i = to_app(e)->get_arg(3); + j = to_app(e)->get_arg(4); + t = to_app(e)->get_arg(5); + return true; + } + else { + return false; + } +} + +expr_ref theory_seq::mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t) { + expr_ref_vector args(m); + args.push_back(s); + args.push_back(tail); + args.push_back(re); + args.push_back(m_autil.mk_int(i)); + args.push_back(m_autil.mk_int(j)); + args.push_back(t); + return expr_ref(m_util.mk_skolem(symbol("aut.step"), args.size(), args.c_ptr(), m.mk_bool_sort()), m); +} + + +/** + acc & s != emp -> \/ step_i_t_j +*/ +void theory_seq::add_accept2step(expr* acc) { + context& ctx = get_context(); + expr* s, *re; + unsigned src; + eautomaton* aut = 0; + VERIFY(is_accept(acc, s, re, src, aut)); + if (!aut) return; + if (m_util.str.is_empty(s)) return; + eautomaton::moves mvs; + aut->get_moves_to(src, mvs); + expr_ref head(m), tail(m), emp(m), step(m); + mk_decompose(s, emp, head, tail); + literal_vector lits; + literal acc_lit = mk_literal(acc); + lits.push_back(~acc_lit); + lits.push_back(mk_eq(emp, s, false)); + for (unsigned i = 0; i < mvs.size(); ++i) { + eautomaton::move mv = mvs[i]; + step = mk_step(s, tail, re, src, mv.dst(), mv.t()); + lits.push_back(mk_literal(step)); + } + TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); +} + + +/** + acc(s, re, i) & step(head, tail, re, i, j, t) => acc(tail, re, j) +*/ + +void theory_seq::add_step2accept(expr* step) { + expr* re, *t, *s, *tail, *i, *j; + VERIFY(is_step(step, s, tail, re, i, j, t)); + expr_ref acc1 = mk_accept(s, re, i); + expr_ref acc2 = mk_accept(tail, re, j); + add_axiom(~mk_literal(acc1), ~mk_literal(step), mk_literal(acc2)); +} + + +/* + rej(s, re, i) & s = t ++ tail => rej(tail, re, j) +*/ +void theory_seq::add_reject2reject(expr* rej) { + expr* s, *re; + unsigned src; + eautomaton* aut = 0; + VERIFY(is_reject(rej, s, re, src, aut)); + if (!aut) return; + if (m_util.str.is_empty(s)) return; + eautomaton::moves mvs; + aut->get_moves_to(src, mvs); + expr_ref head(m), tail(m), emp(m), rej2(m), conc(m); + mk_decompose(s, emp, head, tail); + literal rej1 = mk_literal(rej); + for (unsigned i = 0; i < mvs.size(); ++i) { + eautomaton::move const& mv = mvs[i]; + conc = m_util.str.mk_concat(m_util.str.mk_unit(mv.t()), tail); + rej2 = mk_reject(tail, re, m_autil.mk_int(mv.dst())); + add_axiom(~rej1, ~mk_eq(s, conc, false), mk_literal(rej2)); + } +} + +bool theory_seq::propagate_automata() { + context& ctx = get_context(); + bool change = + (m_accepts_qhead < m_accepts.size()) || + (m_rejects_qhead < m_rejects.size()) || + (m_steps_qhead < m_steps.size()); + + if (change) { + m_trail_stack.push(value_trail(m_accepts_qhead)); + m_trail_stack.push(value_trail(m_rejects_qhead)); + m_trail_stack.push(value_trail(m_steps_qhead)); + } + while (m_accepts_qhead < m_accepts.size() && !ctx.inconsistent()) { + expr* acc = m_accepts[m_accepts_qhead]; + lbool r = ctx.get_assignment(acc); + SASSERT(l_undef != r); + if (r == l_true) { + add_accept2step(acc); + } + ++m_accepts_qhead; + } + while (m_rejects_qhead < m_rejects.size() && !ctx.inconsistent()) { + expr* rej = m_rejects[m_rejects_qhead]; + lbool r = ctx.get_assignment(rej); + SASSERT(l_undef != r); + if (r == l_true) { + add_reject2reject(rej); + } + ++m_rejects_qhead; + } + while (m_steps_qhead < m_steps.size() && !ctx.inconsistent()) { + expr* step = m_steps[m_steps_qhead]; + lbool r = ctx.get_assignment(step); + switch (r) { + case l_true: { + expr* re, *t, *s, *tail, *i, *j; + VERIFY(is_step(step, s, tail, re, i, j, t)); + expr_ref acc1 = mk_accept(s, re, i); + if (ctx.get_assignment(acc1) != l_false) { + add_step2accept(step); + } + break; + } + case l_false: + break; + default: + UNREACHABLE(); + } + ++m_steps_qhead; + } + return change || ctx.inconsistent(); } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index ab0038933..ce5da5cae 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -284,10 +284,13 @@ namespace smt { symbol m_left_sym; // split variable left part symbol m_right_sym; // split variable right part symbol m_accept_sym; + symbol m_reject_sym; // maintain automata with regular expressions. scoped_ptr_vector m_automata; obj_map m_re2aut; + ptr_vector m_accepts, m_rejects, m_steps; + unsigned m_accepts_qhead, m_rejects_qhead, m_steps_qhead; virtual final_check_status final_check_eh(); virtual bool internalize_atom(app*, bool); @@ -332,7 +335,8 @@ namespace smt { bool unchanged(expr* e, expr_ref_vector& es) const { return es.size() == 1 && es[0] == e; } // asserting consequences - void propagate_lit(enode_pair_dependency* dep, literal lit); + void propagate_lit(enode_pair_dependency* dep, literal lit) { propagate_lit(dep, 0, 0, lit); } + void propagate_lit(enode_pair_dependency* dep, unsigned n, literal const* lits, literal lit); void propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2); void propagate_eq(bool_var v, expr* e1, expr* e2); void set_conflict(enode_pair_dependency* dep, literal_vector const& lits = literal_vector()); @@ -372,16 +376,34 @@ namespace smt { expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); + void mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); // automata utilities + void propagate_in_re(expr* n, bool is_true); eautomaton* get_automaton(expr* e); expr_ref mk_accept(expr* s, expr* re, expr* state); bool is_accept(expr* acc) const { return is_skolem(m_accept_sym, acc); } - bool is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut); + bool is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { + return is_acc_rej(m_accept_sym, acc, s, re, i, aut); + } + expr_ref mk_reject(expr* s, expr* re, expr* state); + bool is_reject(expr* rej) const { return is_skolem(m_reject_sym, rej); } + bool is_reject(expr* rej, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { + return is_acc_rej(m_reject_sym, rej, s, re, i, aut); + } + bool is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsigned& i, eautomaton*& aut); + expr_ref mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t); + bool is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const; + bool is_step(expr* e) const; + void propagate_step(bool_var v, expr* n); + void add_reject2reject(expr* rej); + void add_accept2step(expr* acc); + void add_step2accept(expr* step); + bool propagate_automata(); // diagnostics void display_equations(std::ostream& out) const; From 4a5b645d88c68a7d446a7d7096c4fbaffb79681c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Dec 2015 05:37:24 -0800 Subject: [PATCH 13/35] automata Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 195 ++++++++++++++++++----------------------- src/smt/theory_seq.h | 33 ++++--- 2 files changed, 99 insertions(+), 129 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index ee2b3070e..0b8e71f1c 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -223,13 +223,24 @@ theory_seq::theory_seq(ast_manager& m): m_accepts_qhead(0), m_rejects_qhead(0), m_steps_qhead(0) { - m_prefix_sym = "seq.prefix.suffix"; - m_suffix_sym = "seq.suffix.prefix"; - m_left_sym = "seq.left"; - m_right_sym = "seq.right"; - m_contains_left_sym = "seq.contains.left"; - m_contains_right_sym = "seq.contains.right"; - m_accept_sym = "aut.accept"; + m_prefix = "seq.prefix.suffix"; + m_suffix = "seq.suffix.prefix"; + m_left = "seq.left"; + m_right = "seq.right"; + m_contains_left = "seq.contains.left"; + m_contains_right = "seq.contains.right"; + m_accept = "aut.accept"; + m_reject = "aut.reject"; + m_tail = "seq.tail"; + m_head_elem = "seq.head.elem"; + m_seq_first = "seq.first"; + m_seq_last = "seq.last"; + m_indexof_left = "seq.indexof.left"; + m_indexof_right = "seq.indexof.right"; + m_aut_step = "aut.step"; + m_extract_prefix = "seq.extract.prefix"; + m_at_left = "seq.at.left"; + m_at_right = "seq.at.right"; } theory_seq::~theory_seq() { @@ -252,9 +263,6 @@ final_check_status theory_seq::final_check_eh() { if (ctx.inconsistent()) { return FC_CONTINUE; } - if (propagate_automata()) { - return FC_CONTINUE; - } if (branch_variable()) { TRACE("seq", tout << "branch\n";); return FC_CONTINUE; @@ -274,6 +282,9 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << "check_length_coherence\n";); return FC_CONTINUE; } + if (propagate_automata()) { + return FC_CONTINUE; + } if (is_solved()) { TRACE("seq", tout << "is_solved\n";); return FC_DONE; @@ -440,8 +451,8 @@ bool theory_seq::check_length_coherence_tbd() { void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail) { sort* char_sort = 0; VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); - tail = mk_skolem(symbol("seq.tail"), e); - expr_ref v(mk_skolem(symbol("seq.head.elem"), e, 0, 0, char_sort), m); + tail = mk_skolem(m_tail, e); + expr_ref v(mk_skolem(m_head_elem, e, 0, 0, char_sort), m); head = m_util.str.mk_unit(v); emp = m_util.str.mk_empty(m.get_sort(e)); } @@ -613,15 +624,15 @@ bool theory_seq::is_var(expr* a) { } bool theory_seq::is_left_select(expr* a, expr*& b) { - return is_skolem(m_left_sym, a) && (b = to_app(a)->get_arg(0), true); + return is_skolem(m_left, a) && (b = to_app(a)->get_arg(0), true); } bool theory_seq::is_right_select(expr* a, expr*& b) { - return is_skolem(m_right_sym, a) && (b = to_app(a)->get_arg(0), true); + return is_skolem(m_right, a) && (b = to_app(a)->get_arg(0), true); } bool theory_seq::is_head_elem(expr* e) const { - return is_skolem(symbol("seq.head.elem"), e); + return is_skolem(m_head_elem, e); } void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { @@ -633,13 +644,6 @@ void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { } } -bool theory_seq::simplify_eqs() { - return pre_process_eqs(true); -} - -bool theory_seq::solve_basic_eqs() { - return pre_process_eqs(false); -} bool theory_seq::pre_process_eqs(bool simplify_or_solve) { context& ctx = get_context(); @@ -782,12 +786,6 @@ bool theory_seq::simplify_and_solve_eqs() { return change; } -void theory_seq::internalize_eq_eh(app * atom, bool_var v) { -} - -bool theory_seq::internalize_atom(app* a, bool) { - return internalize_term(a); -} bool theory_seq::internalize_term(app* term) { TRACE("seq", tout << mk_pp(term, m) << "\n";); @@ -1123,8 +1121,8 @@ void theory_seq::deque_axiom(expr* n) { lit or s = "" or !prefix(s, x*s1) */ void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) { - expr_ref s1 = mk_skolem(symbol("seq.first"), s); - expr_ref c = mk_skolem(symbol("seq.last"), s); + expr_ref s1 = mk_skolem(m_seq_first, s); + expr_ref c = mk_skolem(m_seq_last, s); expr_ref s1c(m_util.str.mk_concat(s1, c), m); expr_ref lc(m_util.str.mk_length(c), m); expr_ref one(m_autil.mk_int(1), m); @@ -1178,8 +1176,8 @@ void theory_seq::add_indexof_axiom(expr* i) { offset_ne_zero = ~mk_eq(offset, zero, false); } if (!is_num || r.is_zero()) { - expr_ref x = mk_skolem(m_contains_left_sym, t, s); - expr_ref y = mk_skolem(m_contains_right_sym, t, s); + expr_ref x = mk_skolem(m_contains_left, t, s); + expr_ref y = mk_skolem(m_contains_right, t, s); xsy = m_util.str.mk_concat(x,s,y); literal cnt = mk_literal(m_util.str.mk_contains(t, s)); literal eq_empty = mk_eq(s, emp, false); @@ -1200,8 +1198,8 @@ void theory_seq::add_indexof_axiom(expr* i) { // 0 <= offset & offset < len(t) => len(x) = offset // 0 <= offset & offset < len(t) & ~contains(s, y) => indexof(t, s, offset) = -1 // 0 <= offset & offset < len(t) & contains(s, y) => index(t, s, offset) = indexof(y, s, 0) + len(t) - expr_ref x = mk_skolem(symbol("seq.indexof.left"), t, s, offset); - expr_ref y = mk_skolem(symbol("seq.indexof.right"), t, s, offset); + expr_ref x = mk_skolem(m_indexof_left, t, s, offset); + expr_ref y = mk_skolem(m_indexof_right, t, s, offset); expr_ref indexof(m_util.str.mk_index(y, s, zero), m); // TBD: //literal offset_ge_0 = mk_literal(m_autil.mk_ge(offset, zero)); @@ -1221,8 +1219,8 @@ void theory_seq::add_indexof_axiom(expr* i) { void theory_seq::add_replace_axiom(expr* r) { expr* a, *s, *t; VERIFY(m_util.str.is_replace(r, a, s, t)); - expr_ref x = mk_skolem(m_contains_left_sym, a, s); - expr_ref y = mk_skolem(m_contains_right_sym, a, s); + expr_ref x = mk_skolem(m_contains_left, a, s); + expr_ref y = mk_skolem(m_contains_right, a, s); expr_ref xty(m_util.str.mk_concat(x, t, y), m); expr_ref xsy(m_util.str.mk_concat(x, s, y), m); literal cnt = mk_literal(m_util.str.mk_contains(a ,s)); @@ -1315,12 +1313,10 @@ void theory_seq::add_in_re_axiom(expr* n) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); for (unsigned i = 0; i < a->num_states(); ++i) { - expr_ref acc = mk_accept(emp, e2, m_autil.mk_int(i)); - expr_ref rej = mk_reject(emp, e2, m_autil.mk_int(i)); - literal alit = mk_literal(acc); - literal rlit = mk_literal(rej); - add_axiom(a->is_final_state(i)?alit:~alit); - add_axiom(a->is_final_state(i)?~rlit:rlit); + literal acc = mk_accept(emp, e2, i); + literal rej = mk_reject(emp, e2, i); + add_axiom(a->is_final_state(i)?acc:~acc); + add_axiom(a->is_final_state(i)?~rej:rej); } } @@ -1341,14 +1337,11 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { } for (unsigned i = 0; i < states.size(); ++i) { if (is_true) { - expr_ref acc = mk_accept(e1, e2, m_autil.mk_int(a->init())); - lits.push_back(mk_literal(acc)); + lits.push_back(mk_accept(e1, e2, states[i])); } else { - expr_ref rej = mk_reject(e1, e2, m_autil.mk_int(a->init())); - literal rlit = mk_literal(rej); literal nlit = ~lit; - propagate_lit(0, 1, &nlit, rlit); + propagate_lit(0, 1, &nlit, mk_reject(e1, e2, states[i])); } } if (is_true) { @@ -1393,7 +1386,7 @@ enode* theory_seq::ensure_enode(expr* e) { void theory_seq::add_extract_axiom(expr* e) { expr* s, *i, *l; VERIFY(m_util.str.is_extract(e, s, i, l)); - expr_ref x(mk_skolem(symbol("seq.extract.prefix"), s, e), m); + expr_ref x(mk_skolem(m_extract_prefix, s, e), m); expr_ref ls(m_util.str.mk_length(s), m); expr_ref lx(m_util.str.mk_length(x), m); expr_ref le(m_util.str.mk_length(e), m); @@ -1422,8 +1415,8 @@ void theory_seq::add_at_axiom(expr* e) { expr* s, *i; VERIFY(m_util.str.is_at(e, s, i)); expr_ref x(m), y(m), lx(m), le(m), xey(m), zero(m), one(m), len_e(m), len_x(m); - x = mk_skolem(symbol("seq.at.left"), s); - y = mk_skolem(symbol("seq.at.right"), s); + x = mk_skolem(m_at_left, s); + y = mk_skolem(m_at_right, s); xey = m_util.str.mk_concat(x, e, y); zero = m_autil.mk_int(0); one = m_autil.mk_int(1); @@ -1512,28 +1505,32 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { expr_ref f(m); if (is_true && m_util.str.is_prefix(e, e1, e2)) { - f = mk_skolem(m_prefix_sym, e1, e2); + f = mk_skolem(m_prefix, e1, e2); f = m_util.str.mk_concat(e1, f); propagate_eq(v, f, e2); } else if (is_true && m_util.str.is_suffix(e, e1, e2)) { - f = mk_skolem(m_suffix_sym, e1, e2); + f = mk_skolem(m_suffix, e1, e2); f = m_util.str.mk_concat(f, e1); propagate_eq(v, f, e2); } else if (is_true && m_util.str.is_contains(e, e1, e2)) { - expr_ref f1 = mk_skolem(m_contains_left_sym, e1, e2); - expr_ref f2 = mk_skolem(m_contains_right_sym, e1, e2); + expr_ref f1 = mk_skolem(m_contains_left, e1, e2); + expr_ref f2 = mk_skolem(m_contains_right, e1, e2); f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2); propagate_eq(v, f, e1); } else if (is_accept(e)) { - m_trail_stack.push(push_back_vector >(m_accepts)); - m_accepts.push_back(e); + if (is_true) { + m_trail_stack.push(push_back_vector >(m_accepts)); + m_accepts.push_back(e); + } } else if (is_reject(e)) { - m_trail_stack.push(push_back_vector >(m_rejects)); - m_rejects.push_back(e); + if (is_true) { + m_trail_stack.push(push_back_vector >(m_rejects)); + m_rejects.push_back(e); + } } else if (is_step(e)) { if (is_true) { @@ -1659,11 +1656,11 @@ eautomaton* theory_seq::get_automaton(expr* re) { return result; } -expr_ref theory_seq::mk_accept(expr* s, expr* re, expr* state) { - return expr_ref(mk_skolem(m_accept_sym, s, re, state, m.mk_bool_sort()), m); +literal theory_seq::mk_accept(expr* s, expr* re, expr* state) { + return mk_literal(mk_skolem(m_accept, s, re, state, m.mk_bool_sort())); } -expr_ref theory_seq::mk_reject(expr* s, expr* re, expr* state) { - return expr_ref(mk_skolem(m_reject_sym, s, re, state, m.mk_bool_sort()), m); +literal theory_seq::mk_reject(expr* s, expr* re, expr* state) { + return mk_literal(mk_skolem(m_reject, s, re, state, m.mk_bool_sort())); } bool theory_seq::is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { @@ -1683,7 +1680,7 @@ bool theory_seq::is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsi } bool theory_seq::is_step(expr* e) const { - return is_skolem(symbol("aut.step"), e); + return is_skolem(m_aut_step, e); } bool theory_seq::is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const { @@ -1709,7 +1706,7 @@ expr_ref theory_seq::mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned args.push_back(m_autil.mk_int(i)); args.push_back(m_autil.mk_int(j)); args.push_back(t); - return expr_ref(m_util.mk_skolem(symbol("aut.step"), args.size(), args.c_ptr(), m.mk_bool_sort()), m); + return expr_ref(m_util.mk_skolem(m_aut_step, args.size(), args.c_ptr(), m.mk_bool_sort()), m); } @@ -1718,6 +1715,7 @@ expr_ref theory_seq::mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned */ void theory_seq::add_accept2step(expr* acc) { context& ctx = get_context(); + SASSERT(ctx.get_assignment(acc) == l_true); expr* s, *re; unsigned src; eautomaton* aut = 0; @@ -1729,8 +1727,7 @@ void theory_seq::add_accept2step(expr* acc) { expr_ref head(m), tail(m), emp(m), step(m); mk_decompose(s, emp, head, tail); literal_vector lits; - literal acc_lit = mk_literal(acc); - lits.push_back(~acc_lit); + lits.push_back(~ctx.get_literal(acc)); lits.push_back(mk_eq(emp, s, false)); for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move mv = mvs[i]; @@ -1747,11 +1744,13 @@ void theory_seq::add_accept2step(expr* acc) { */ void theory_seq::add_step2accept(expr* step) { + context& ctx = get_context(); + SASSERT(ctx.get_assignment(step) == l_true); expr* re, *t, *s, *tail, *i, *j; VERIFY(is_step(step, s, tail, re, i, j, t)); - expr_ref acc1 = mk_accept(s, re, i); - expr_ref acc2 = mk_accept(tail, re, j); - add_axiom(~mk_literal(acc1), ~mk_literal(step), mk_literal(acc2)); + literal acc1 = mk_accept(s, re, i); + literal acc2 = mk_accept(tail, re, j); + add_axiom(~acc1, ~ctx.get_literal(step), acc2); } @@ -1759,6 +1758,8 @@ void theory_seq::add_step2accept(expr* step) { rej(s, re, i) & s = t ++ tail => rej(tail, re, j) */ void theory_seq::add_reject2reject(expr* rej) { + context& ctx = get_context(); + SASSERT(ctx.get_assignment(rej) == l_true); expr* s, *re; unsigned src; eautomaton* aut = 0; @@ -1767,65 +1768,37 @@ void theory_seq::add_reject2reject(expr* rej) { if (m_util.str.is_empty(s)) return; eautomaton::moves mvs; aut->get_moves_to(src, mvs); - expr_ref head(m), tail(m), emp(m), rej2(m), conc(m); + expr_ref head(m), tail(m), emp(m), conc(m); mk_decompose(s, emp, head, tail); - literal rej1 = mk_literal(rej); + literal rej1 = ctx.get_literal(rej); for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move const& mv = mvs[i]; conc = m_util.str.mk_concat(m_util.str.mk_unit(mv.t()), tail); - rej2 = mk_reject(tail, re, m_autil.mk_int(mv.dst())); - add_axiom(~rej1, ~mk_eq(s, conc, false), mk_literal(rej2)); + literal rej2 = mk_reject(tail, re, m_autil.mk_int(mv.dst())); + add_axiom(~rej1, ~mk_eq(s, conc, false), rej2); } } bool theory_seq::propagate_automata() { context& ctx = get_context(); - bool change = - (m_accepts_qhead < m_accepts.size()) || - (m_rejects_qhead < m_rejects.size()) || - (m_steps_qhead < m_steps.size()); + bool change = false; + if (m_accepts_qhead < m_accepts.size()) + m_trail_stack.push(value_trail(m_accepts_qhead)), change = true; + if (m_rejects_qhead < m_rejects.size()) + m_trail_stack.push(value_trail(m_rejects_qhead)), change = true; + if (m_steps_qhead < m_steps.size()) + m_trail_stack.push(value_trail(m_steps_qhead)), change = true; - if (change) { - m_trail_stack.push(value_trail(m_accepts_qhead)); - m_trail_stack.push(value_trail(m_rejects_qhead)); - m_trail_stack.push(value_trail(m_steps_qhead)); - } while (m_accepts_qhead < m_accepts.size() && !ctx.inconsistent()) { - expr* acc = m_accepts[m_accepts_qhead]; - lbool r = ctx.get_assignment(acc); - SASSERT(l_undef != r); - if (r == l_true) { - add_accept2step(acc); - } + add_accept2step(m_accepts[m_accepts_qhead]); ++m_accepts_qhead; } while (m_rejects_qhead < m_rejects.size() && !ctx.inconsistent()) { - expr* rej = m_rejects[m_rejects_qhead]; - lbool r = ctx.get_assignment(rej); - SASSERT(l_undef != r); - if (r == l_true) { - add_reject2reject(rej); - } + add_reject2reject(m_rejects[m_rejects_qhead]); ++m_rejects_qhead; } while (m_steps_qhead < m_steps.size() && !ctx.inconsistent()) { - expr* step = m_steps[m_steps_qhead]; - lbool r = ctx.get_assignment(step); - switch (r) { - case l_true: { - expr* re, *t, *s, *tail, *i, *j; - VERIFY(is_step(step, s, tail, re, i, j, t)); - expr_ref acc1 = mk_accept(s, re, i); - if (ctx.get_assignment(acc1) != l_false) { - add_step2accept(step); - } - break; - } - case l_false: - break; - default: - UNREACHABLE(); - } + add_step2accept(m_steps[m_steps_qhead]); ++m_steps_qhead; } return change || ctx.inconsistent(); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index ce5da5cae..f19599472 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -277,14 +277,9 @@ namespace smt { arith_util m_autil; th_trail_stack m_trail_stack; stats m_stats; - symbol m_prefix_sym; - symbol m_suffix_sym; - symbol m_contains_left_sym; - symbol m_contains_right_sym; - symbol m_left_sym; // split variable left part - symbol m_right_sym; // split variable right part - symbol m_accept_sym; - symbol m_reject_sym; + symbol m_prefix, m_suffix, m_contains_left, m_contains_right, m_left, m_right, m_accept, m_reject; + symbol m_tail, m_head_elem, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; + symbol m_extract_prefix, m_at_left, m_at_right; // maintain automata with regular expressions. scoped_ptr_vector m_automata; @@ -293,9 +288,9 @@ namespace smt { unsigned m_accepts_qhead, m_rejects_qhead, m_steps_qhead; virtual final_check_status final_check_eh(); - virtual bool internalize_atom(app*, bool); + virtual bool internalize_atom(app* atom, bool) { return internalize_term(atom); } virtual bool internalize_term(app*); - virtual void internalize_eq_eh(app * atom, bool_var v); + virtual void internalize_eq_eh(app * atom, bool_var v) {} virtual void new_eq_eh(theory_var, theory_var); virtual void new_diseq_eh(theory_var, theory_var); virtual void assign_eh(bool_var v, bool is_true); @@ -325,10 +320,10 @@ namespace smt { bool check_ineq_coherence(); bool pre_process_eqs(bool simplify_or_solve); - bool simplify_eqs(); + bool simplify_eqs() { return pre_process_eqs(true); } + bool solve_basic_eqs() { return pre_process_eqs(false); } bool simplify_eq(expr* l, expr* r, enode_pair_dependency* dep); bool solve_unit_eq(expr* l, expr* r, enode_pair_dependency* dep); - bool solve_basic_eqs(); bool solve_nqs(); bool solve_ne(unsigned i); @@ -385,15 +380,17 @@ namespace smt { // automata utilities void propagate_in_re(expr* n, bool is_true); eautomaton* get_automaton(expr* e); - expr_ref mk_accept(expr* s, expr* re, expr* state); - bool is_accept(expr* acc) const { return is_skolem(m_accept_sym, acc); } + literal mk_accept(expr* s, expr* re, expr* state); + literal mk_accept(expr* s, expr* re, unsigned i) { return mk_accept(s, re, m_autil.mk_int(i)); } + bool is_accept(expr* acc) const { return is_skolem(m_accept, acc); } bool is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { - return is_acc_rej(m_accept_sym, acc, s, re, i, aut); + return is_acc_rej(m_accept, acc, s, re, i, aut); } - expr_ref mk_reject(expr* s, expr* re, expr* state); - bool is_reject(expr* rej) const { return is_skolem(m_reject_sym, rej); } + literal mk_reject(expr* s, expr* re, expr* state); + literal mk_reject(expr* s, expr* re, unsigned i) { return mk_reject(s, re, m_autil.mk_int(i)); } + bool is_reject(expr* rej) const { return is_skolem(m_reject, rej); } bool is_reject(expr* rej, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { - return is_acc_rej(m_reject_sym, rej, s, re, i, aut); + return is_acc_rej(m_reject, rej, s, re, i, aut); } bool is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsigned& i, eautomaton*& aut); expr_ref mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t); From 31302ec851204b1244df4bb0aca0fe502ebece39 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 25 Dec 2015 15:22:26 -0800 Subject: [PATCH 14/35] automata Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 194 +++++++++++++++++++++++++++++- src/ast/rewriter/seq_rewriter.h | 15 +++ src/ast/seq_decl_plugin.cpp | 8 +- src/ast/seq_decl_plugin.h | 5 +- src/math/automata/automaton.h | 152 ++++++++++++++++++----- src/smt/theory_seq.cpp | 134 ++++++++------------- src/smt/theory_seq.h | 12 +- 7 files changed, 386 insertions(+), 134 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 41c20f599..428c4b224 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -25,6 +25,89 @@ Notes: #include"automaton.h" + +re2automaton::re2automaton(ast_manager& m): m(m), u(m) {} + +eautomaton* re2automaton::operator()(expr* e) { + eautomaton* r = re2aut(e); + if (r) { + r->compress(); + } + return r; +} + +eautomaton* re2automaton::re2aut(expr* e) { + SASSERT(u.is_re(e)); + expr* e1, *e2; + scoped_ptr a, b; + if (u.re.is_to_re(e, e1)) { + return seq2aut(e1); + } + else if (u.re.is_concat(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { + return eautomaton::mk_concat(*a, *b); + } + else if (u.re.is_union(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { + return eautomaton::mk_union(*a, *b); + } + else if (u.re.is_star(e, e1) && (a = re2aut(e1))) { + a->add_final_to_init_moves(); + a->add_init_to_final_states(); + return a.detach(); + } + else if (u.re.is_plus(e, e1) && (a = re2aut(e1))) { + a->add_final_to_init_moves(); + return a.detach(); + } + else if (u.re.is_opt(e, e1) && (a = re2aut(e1))) { + a = eautomaton::mk_opt(*a); + return a.detach(); + } + else if (u.re.is_range(e)) { + + } + else if (u.re.is_loop(e)) { + + } +#if 0 + else if (u.re.is_intersect(e, e1, e2)) { + + } + else if (u.re.is_empty(e)) { + + } +#endif + + return 0; +} + +eautomaton* re2automaton::seq2aut(expr* e) { + SASSERT(u.is_seq(e)); + zstring s; + expr* e1, *e2; + scoped_ptr a, b; + if (u.str.is_concat(e, e1, e2) && (a = seq2aut(e1)) && (b = seq2aut(e2))) { + return eautomaton::mk_concat(*a, *b); + } + else if (u.str.is_unit(e, e1)) { + return alloc(eautomaton, m, e1); + } + else if (u.str.is_empty(e)) { + return eautomaton::mk_epsilon(m); + } + else if (u.str.is_string(e, s)) { + unsigned init = 0; + eautomaton::moves mvs; + unsigned_vector final; + final.push_back(s.length()); + for (unsigned k = 0; k < s.length(); ++k) { + // reference count? + mvs.push_back(eautomaton::move(m, k, k+1, u.str.mk_char(s, k))); + } + return alloc(eautomaton, m, init, final, mvs); + } + return 0; +} + br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { SASSERT(f->get_family_id() == get_fid()); @@ -159,11 +242,15 @@ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) { result = a; return BR_DONE; } - if (m_util.str.is_concat(a, c, d) && - m_util.str.is_string(d, s1) && isc2) { + // TBD concatenation is right-associative + if (isc2 && m_util.str.is_concat(a, c, d) && m_util.str.is_string(d, s1)) { result = m_util.str.mk_concat(c, m_util.str.mk_string(s1 + s2)); return BR_DONE; } + if (isc1 && m_util.str.is_concat(b, c, d) && m_util.str.is_string(c, s2)) { + result = m_util.str.mk_concat(m_util.str.mk_string(s1 + s2), d); + return BR_DONE; + } return BR_FAILED; } @@ -398,7 +485,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { result = m().mk_eq(m_util.str.mk_empty(m().get_sort(a)), a); return BR_REWRITE3; } - // concatenation is left-associative, so a2, b2 are not concatenations + // TBD concatenation is right-associative expr* a1, *a2, *b1, *b2; if (m_util.str.is_concat(a, a1, a2) && m_util.str.is_concat(b, b1, b2) && a2 == b2) { @@ -498,7 +585,108 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { } return BR_FAILED; } + +void seq_rewriter::add_next(u_map& next, unsigned idx, expr* cond) { + expr* acc; + if (m().is_true(cond) || !next.find(idx, acc)) { + next.insert(idx, cond); + } + else { + next.insert(idx, m().mk_or(cond, acc)); + } +} + +bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { + zstring s; + ptr_vector todo; + expr *e1, *e2; + todo.push_back(e); + while (!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (m_util.str.is_string(e, s)) { + for (unsigned i = s.length(); i > 0; ) { + --i; + seq.push_back(m_util.str.mk_char(s, i)); + } + } + else if (m_util.str.is_empty(e)) { + continue; + } + else if (m_util.str.is_unit(e)) { + seq.push_back(e); + } + else if (m_util.str.is_concat(e, e1, e2)) { + todo.push_back(e1); + todo.push_back(e2); + } + else { + return false; + } + } + seq.reverse(); + return true; +} + br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { + scoped_ptr aut; + expr_ref_vector seq(m()); + if (is_sequence(a, seq) && (aut = re2automaton(m())(b))) { + expr_ref_vector trail(m()); + u_map maps[2]; + bool select_map = false; + expr_ref ch(m()), cond(m()); + eautomaton::moves mvs; + maps[0].insert(aut->init(), m().mk_true()); + // is_accepted(a, aut) & some state in frontier is final. + + for (unsigned i = 0; i < seq.size(); ++i) { + u_map& frontier = maps[select_map]; + u_map& next = maps[!select_map]; + select_map = !select_map; + ch = seq[i].get(); + next.reset(); + u_map::iterator it = frontier.begin(), end = frontier.end(); + for (; it != end; ++it) { + mvs.reset(); + unsigned state = it->m_key; + expr* acc = it->m_value; + aut->get_moves_from(state, mvs, false); + for (unsigned j = 0; j < mvs.size(); ++j) { + eautomaton::move const& mv = mvs[j]; + if (m().is_value(mv.t()) && m().is_value(ch)) { + if (mv.t() == ch) { + add_next(next, mv.dst(), acc); + } + else { + continue; + } + } + else { + cond = m().mk_eq(mv.t(), ch); + if (!m().is_true(acc)) cond = m().mk_and(acc, cond); + add_next(next, mv.dst(), cond); + } + } + } + } + u_map const& frontier = maps[select_map]; + u_map::iterator it = frontier.begin(), end = frontier.end(); + expr_ref_vector ors(m()); + for (; it != end; ++it) { + unsigned_vector states; + bool has_final = false; + aut->get_epsilon_closure(it->m_key, states); + for (unsigned i = 0; i < states.size() && !has_final; ++i) { + has_final = aut->is_final_state(states[i]); + } + if (has_final) { + ors.push_back(it->m_value); + } + } + result = mk_or(ors); + return BR_REWRITE_FULL; + } return BR_FAILED; } br_status seq_rewriter::mk_str_to_regexp(expr* a, expr_ref& result) { diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index c3e466585..f6772bfe9 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -24,8 +24,20 @@ Notes: #include"rewriter_types.h" #include"params.h" #include"lbool.h" +#include"automaton.h" +typedef automaton eautomaton; +class re2automaton { + ast_manager& m; + seq_util u; + eautomaton* re2aut(expr* e); + eautomaton* seq2aut(expr* e); + public: + re2automaton(ast_manager& m); + eautomaton* operator()(expr* e); +}; + /** \brief Cheap rewrite rules for seq constraints */ @@ -61,6 +73,9 @@ class seq_rewriter { bool min_length(unsigned n, expr* const* es, unsigned& len); expr* concat_non_empty(unsigned n, expr* const* es); + void add_next(u_map& next, unsigned idx, expr* cond); + bool is_sequence(expr* e, expr_ref_vector& seq); + public: seq_rewriter(ast_manager & m, params_ref const & p = params_ref()): m_util(m), m_autil(m), m_es(m), m_lhs(m), m_rhs(m) { diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 10e9c00a5..e516d4ea5 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -223,9 +223,9 @@ bool seq_decl_plugin::match(ptr_vector& binding, sort* s, sort* sP) { } /* - \brief match left associative operator. + \brief match right associative operator. */ -void seq_decl_plugin::match_left_assoc(psig& sig, unsigned dsz, sort *const* dom, sort* range, sort_ref& range_out) { +void seq_decl_plugin::match_right_assoc(psig& sig, unsigned dsz, sort *const* dom, sort* range, sort_ref& range_out) { ptr_vector binding; ast_manager& m = *m_manager; TRACE("seq_verbose", @@ -441,9 +441,9 @@ func_decl* seq_decl_plugin::mk_assoc_fun(decl_kind k, unsigned arity, sort* cons if (arity == 0) { m.raise_exception("Invalid function application. At least one argument expected"); } - match_left_assoc(*m_sigs[k], arity, domain, range, rng); + match_right_assoc(*m_sigs[k], arity, domain, range, rng); func_decl_info info(m_family_id, k_seq); - info.set_left_associative(); + info.set_right_associative(); return m.mk_func_decl(m_sigs[(rng == m_string)?k_string:k_seq]->m_name, rng, rng, rng, info); } diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 9a5f65d05..d0a475b25 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -137,7 +137,7 @@ class seq_decl_plugin : public decl_plugin { void match(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& rng); - void match_left_assoc(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& rng); + void match_right_assoc(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& rng); bool match(ptr_vector& binding, sort* s, sort* sP); @@ -221,7 +221,7 @@ public: app* mk_char(char ch); app* mk_concat(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); } app* mk_concat(expr* a, expr* b, expr* c) { - return mk_concat(mk_concat(a, b), c); + return mk_concat(a, mk_concat(b, c)); } expr* mk_concat(unsigned n, expr* const* es) { if (n == 1) return es[0]; SASSERT(n > 1); return m.mk_app(m_fid, OP_SEQ_CONCAT, n, es); } app* mk_length(expr* a) { return m.mk_app(m_fid, OP_SEQ_LENGTH, 1, &a); } @@ -278,6 +278,7 @@ public: void get_concat(expr* e, expr_ref_vector& es) const; expr* get_leftmost_concat(expr* e) const { expr* e1, *e2; while (is_concat(e, e1, e2)) e = e1; return e; } + expr* get_rightmost_concat(expr* e) const { expr* e1, *e2; while (is_concat(e, e1, e2)) e = e2; return e; } }; class re { diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index 684082170..dabfa1417 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -100,8 +100,7 @@ public: automaton(M& m, unsigned init, unsigned_vector const& final, moves const& mvs): m(m) { m_init = init; for (unsigned i = 0; i < final.size(); ++i) { - m_final_states.push_back(final[i]); - m_final_set.insert(final[i]); + add_to_final_states(final[i]); } for (unsigned i = 0; i < mvs.size(); ++i) { move const& mv = mvs[i]; @@ -110,8 +109,7 @@ public: m_delta.resize(n+1, moves()); m_delta_inv.resize(n+1, moves()); } - m_delta[mv.src()].push_back(mv); - m_delta_inv[mv.dst()].push_back(mv); + add(mv); } } @@ -125,8 +123,7 @@ public: m_delta[i].push_back(move(m, i, i + 1, seq[i])); m_delta[i + 1].push_back(move(m, i, i + 1, seq[i])); } - m_final_states.push_back(seq.size()); - m_final_set.insert(seq.size()); + add_to_final_states(seq.size()); } // The automaton that accepts t @@ -135,10 +132,8 @@ public: m_init(0) { m_delta.resize(2, moves()); m_delta_inv.resize(2, moves()); - m_final_set.insert(1); - m_final_states.push_back(1); - m_delta[0].push_back(move(m, 0, 1, t)); - m_delta_inv[1].push_back(move(m, 0, 1, t)); + add_to_final_states(1); + add(move(m, 0, 1, t)); } automaton(automaton const& other): @@ -257,13 +252,24 @@ public: return alloc(automaton, m, init, final, mvs); } - void add_init_to_final() { - if (!m_final_set.contains(m_init)) { - m_final_set.insert(m_init); - m_final_states.push_back(m_init); + void add_to_final_states(unsigned s) { + if (!is_final_state(s)) { + m_final_set.insert(s); + m_final_states.push_back(s); } } + void remove_from_final_states(unsigned s) { + if (is_final_state(s)) { + m_final_set.remove(s); + m_final_states.erase(s); + } + } + + void add_init_to_final_states() { + add_to_final_states(init()); + } + void add_final_to_init_moves() { for (unsigned i = 0; i < m_final_states.size(); ++i) { unsigned state = m_final_states[i]; @@ -273,16 +279,69 @@ public: found = (mvs[j].dst() == m_init) && mvs[j].is_epsilon(); } if (!found) { - m_delta[state].push_back(move(m, state, m_init)); - m_delta_inv[m_init].push_back(move(m, state, m_init)); + add(move(m, state, m_init)); } } } - // remove states that only have epsilon transitions. + // remove epsilon transitions + // src - e -> dst + // in_degree(src) = 1, final(src) => final(dst), src0 != src + // src0 - t -> src - e -> dst => src0 - t -> dst + // out_degree(dst) = 1, final(dst) => final(src), dst != dst1 + // src - e -> dst - t -> dst1 => src - t -> dst1 void compress() { - - // TBD + for (unsigned i = 0; i < m_delta.size(); ++i) { + for (unsigned j = 0; j < m_delta[i].size(); ++j) { + move const& mv = m_delta[i][j]; + unsigned src = mv.src(); + unsigned dst = mv.dst(); + SASSERT(src == i); + if (mv.is_epsilon()) { + if (src == dst) { + // just remove this edge. + } + else if (1 == in_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) { + move const& mv0 = m_delta_inv[src][0]; + unsigned src0 = mv0.src(); + T* t = mv0.t(); + SASSERT(mv0.dst() == src); + if (src0 == src) { + continue; + } + add(move(m, src0, dst, t)); + remove(src0, src, t); + } + else if (1 == out_degree(dst) && init() != dst && (!is_final_state(dst) || is_final_state(src))) { + move const& mv1 = m_delta[dst][0]; + unsigned dst1 = mv1.dst(); + T* t = mv1.t(); + SASSERT(mv1.src() == dst); + if (dst1 == dst) { + continue; + } + add(move(m, src, dst1, t)); + remove(dst, dst1, t); + } + else { + continue; + } + remove(src, dst, 0); + --j; + } + } + } + while (true) { + SASSERT(!m_delta.empty()); + unsigned src = m_delta.size() - 1; + if (in_degree(src) == 0 && init() != src) { + remove_from_final_states(src); + m_delta.pop_back(); + } + else { + break; + } + } } bool is_sequence(unsigned& length) const { @@ -356,11 +415,11 @@ public: void get_inv_epsilon_closure(unsigned state, unsigned_vector& states) { get_epsilon_closure(state, m_delta_inv, states); } - void get_moves_from(unsigned state, moves& mvs) const { - get_moves(state, m_delta, mvs); + void get_moves_from(unsigned state, moves& mvs, bool epsilon_closure = true) const { + get_moves(state, m_delta, mvs, epsilon_closure); } - void get_moves_to(unsigned state, moves& mvs) { - get_moves(state, m_delta_inv, mvs); + void get_moves_to(unsigned state, moves& mvs, bool epsilon_closure = true) { + get_moves(state, m_delta_inv, mvs, epsilon_closure); } template @@ -384,9 +443,41 @@ public: return out; } private: + + void add(move const& mv) { + m_delta[mv.src()].push_back(mv); + m_delta_inv[mv.dst()].push_back(mv); + } + + + unsigned find_move(unsigned src, unsigned dst, T* t, moves const& mvs) { + for (unsigned i = 0; i < mvs.size(); ++i) { + move const& mv = mvs[i]; + if (mv.src() == src && mv.dst() == dst && t == mv.t()) { + return i; + } + } + UNREACHABLE(); + return UINT_MAX; + } + + void remove(unsigned src, unsigned dst, T* t, moves& mvs) { + remove(find_move(src, dst, t, mvs), mvs); + } + + void remove(unsigned src, unsigned dst, T* t) { + remove(src, dst, t, m_delta[src]); + remove(src, dst, t, m_delta_inv[dst]); + } + + void remove(unsigned index, moves& mvs) { + mvs[index] = mvs.back(); + mvs.pop_back(); + } + mutable unsigned_vector m_states1, m_states2; - void get_moves(unsigned state, vector const& delta, moves& mvs) const { + void get_moves(unsigned state, vector const& delta, moves& mvs, bool epsilon_closure) const { m_states1.reset(); m_states2.reset(); get_epsilon_closure(state, delta, m_states1); @@ -396,10 +487,15 @@ private: for (unsigned j = 0; j < mv1.size(); ++j) { move const& mv = mv1[j]; if (!mv.is_epsilon()) { - m_states2.reset(); - get_epsilon_closure(mv.dst(), delta, m_states2); - for (unsigned k = 0; k < m_states2.size(); ++k) { - mvs.push_back(move(m, mv.src(), m_states2[k], mv.t())); + if (epsilon_closure) { + m_states2.reset(); + get_epsilon_closure(mv.dst(), delta, m_states2); + for (unsigned k = 0; k < m_states2.size(); ++k) { + mvs.push_back(move(m, state, m_states2[k], mv.t())); + } + } + else { + mvs.push_back(move(m, state, mv.dst(), mv.t())); } } } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 0b8e71f1c..39ed71beb 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -35,79 +35,6 @@ struct display_expr { }; -re2automaton::re2automaton(ast_manager& m): m(m), u(m) {} - -eautomaton* re2automaton::re2aut(expr* e) { - SASSERT(u.is_re(e)); - expr* e1, *e2; - scoped_ptr a, b; - if (u.re.is_to_re(e, e1)) { - return seq2aut(e1); - } - else if (u.re.is_concat(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { - return eautomaton::mk_concat(*a, *b); - } - else if (u.re.is_union(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { - return eautomaton::mk_union(*a, *b); - } - else if (u.re.is_star(e, e1) && (a = re2aut(e1))) { - a->add_final_to_init_moves(); - a->add_init_to_final(); - return a.detach(); - } - else if (u.re.is_plus(e, e1) && (a = re2aut(e1))) { - a->add_final_to_init_moves(); - return a.detach(); - } - else if (u.re.is_opt(e, e1) && (a = re2aut(e1))) { - a = eautomaton::mk_opt(*a); - return a.detach(); - } - else if (u.re.is_range(e)) { - - } - else if (u.re.is_loop(e)) { - - } -#if 0 - else if (u.re.is_intersect(e, e1, e2)) { - - } - else if (u.re.is_empty(e)) { - - } -#endif - - return 0; -} - -eautomaton* re2automaton::seq2aut(expr* e) { - SASSERT(u.is_seq(e)); - zstring s; - expr* e1, *e2; - scoped_ptr a, b; - if (u.str.is_concat(e, e1, e2) && (a = seq2aut(e1)) && (b = seq2aut(e2))) { - return eautomaton::mk_concat(*a, *b); - } - else if (u.str.is_unit(e, e1)) { - return alloc(eautomaton, m, e1); - } - else if (u.str.is_empty(e)) { - return eautomaton::mk_epsilon(m); - } - else if (u.str.is_string(e, s)) { - unsigned init = 0; - eautomaton::moves mvs; - unsigned_vector final; - final.push_back(s.length()); - for (unsigned k = 0; k < s.length(); ++k) { - // reference count? - mvs.push_back(eautomaton::move(m, k, k+1, u.str.mk_char(s, k))); - } - return alloc(eautomaton, m, init, final, mvs); - } - return 0; -} void theory_seq::solution_map::update(expr* e, expr* r, enode_pair_dependency* d) { m_cache.reset(); @@ -449,12 +376,35 @@ bool theory_seq::check_length_coherence_tbd() { } void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail) { + expr* e1, *e2; sort* char_sort = 0; + zstring s; VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); - tail = mk_skolem(m_tail, e); - expr_ref v(mk_skolem(m_head_elem, e, 0, 0, char_sort), m); - head = m_util.str.mk_unit(v); emp = m_util.str.mk_empty(m.get_sort(e)); + if (m_util.str.is_empty(e)) { + head = m_util.str.mk_unit(mk_skolem(m_head_elem, e, 0, 0, char_sort)); + tail = mk_skolem(m_tail, e); + } + else if (m_util.str.is_string(e, s)) { + head = m_util.str.mk_unit(m_util.str.mk_char(s, 0)); + tail = m_util.str.mk_string(s.extract(1, s.length()-1)); + } + else if (m_util.str.is_unit(e)) { + head = e; + tail = emp; + } + else if (m_util.str.is_concat(e, e1, e2) && m_util.str.is_unit(e1)) { + head = e1; + tail = e2; + } + else { + head = m_util.str.mk_unit(mk_skolem(m_head_elem, e, 0, 0, char_sort)); + tail = mk_skolem(m_tail, e); + if (!m_util.is_skolem(e)) { + expr_ref conc(m_util.str.mk_concat(head, tail), m); + add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); + } + } } bool theory_seq::check_ineq_coherence() { @@ -484,8 +434,8 @@ bool theory_seq::is_solved() { if (!check_ineq_coherence()) { return false; } - if (!m_re2aut.empty()) { - return false; + for (unsigned i = 0; i < m_automata.size(); ++i) { + if (!m_automata[i]) return false; } SASSERT(check_length_coherence()); @@ -500,7 +450,7 @@ void theory_seq::propagate_lit(enode_pair_dependency* dep, unsigned n, literal c vector _eqs; m_dm.linearize(dep, _eqs); TRACE("seq", ctx.display_detailed_literal(tout, lit); - tout << " <- "; display_deps(tout, dep);); + tout << " <- "; ctx.display_literals_verbose(tout, n, lits); display_deps(tout, dep);); justification* js = ctx.mk_justification( ext_theory_propagation_justification( @@ -1248,9 +1198,10 @@ void theory_seq::add_elim_string_axiom(expr* n) { zstring s; VERIFY(m_util.str.is_string(n, s)); SASSERT(s.length() > 0); - expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, 0)), m); - for (unsigned i = 1; i < s.length(); ++i) { - result = m_util.str.mk_concat(result, m_util.str.mk_unit(m_util.str.mk_char(s, i))); + expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, s.length()-1)), m); + for (unsigned i = s.length()-1; i > 0; ) { + --i; + result = m_util.str.mk_concat(m_util.str.mk_unit(m_util.str.mk_char(s, i)), result); } add_axiom(mk_eq(n, result, false)); m_rep.update(n, result, 0); @@ -1322,6 +1273,7 @@ void theory_seq::add_in_re_axiom(expr* n) { void theory_seq::propagate_in_re(expr* n, bool is_true) { + TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); expr* e1, *e2; VERIFY(m_util.str.is_in_re(n, e1, e2)); eautomaton* a = get_automaton(e2); @@ -1349,6 +1301,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { propagate_lit(0, 1, &lit, lits[1]); } else { + TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr());); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } } @@ -1435,10 +1388,15 @@ void theory_seq::add_at_axiom(expr* e) { step(s, tail, re, i, j, t) -> s = t ++ tail */ void theory_seq::propagate_step(bool_var v, expr* step) { + context& ctx = get_context(); expr* re, *t, *s, *tail, *i, *j; VERIFY(is_step(step, s, tail, re, i, j, t)); expr_ref conc(m_util.str.mk_concat(m_util.str.mk_unit(t), tail), m); + expr_ref sr(s, m); propagate_eq(v, s, conc); + enode* n1 = ensure_enode(step); + enode* n2 = ctx.get_enode(m.mk_true()); + m_eqs.push_back(eq(sr, conc, m_dm.mk_leaf(enode_pair(n1, n2)))); } @@ -1479,12 +1437,13 @@ bool theory_seq::is_skolem(symbol const& s, expr* e) const { void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { context& ctx = get_context(); - SASSERT(ctx.e_internalized(e2)); enode* n1 = ensure_enode(e1); enode* n2 = ensure_enode(e2); if (n1->get_root() == n2->get_root()) { return; } + ctx.mark_as_relevant(n1); + ctx.mark_as_relevant(n2); TRACE("seq", tout << mk_pp(ctx.bool_var2enode(v)->get_owner(), m) << " => " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); @@ -1517,7 +1476,7 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { else if (is_true && m_util.str.is_contains(e, e1, e2)) { expr_ref f1 = mk_skolem(m_contains_left, e1, e2); expr_ref f2 = mk_skolem(m_contains_right, e1, e2); - f = m_util.str.mk_concat(m_util.str.mk_concat(f1, e2), f2); + f = m_util.str.mk_concat(f1, m_util.str.mk_concat(e2, f2)); propagate_eq(v, f, e1); } else if (is_accept(e)) { @@ -1723,7 +1682,7 @@ void theory_seq::add_accept2step(expr* acc) { if (!aut) return; if (m_util.str.is_empty(s)) return; eautomaton::moves mvs; - aut->get_moves_to(src, mvs); + aut->get_moves_from(src, mvs); expr_ref head(m), tail(m), emp(m), step(m); mk_decompose(s, emp, head, tail); literal_vector lits; @@ -1735,6 +1694,9 @@ void theory_seq::add_accept2step(expr* acc) { lits.push_back(mk_literal(step)); } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + for (unsigned i = 0; i < lits.size(); ++i) { // TBD + ctx.mark_as_relevant(lits[i]); + } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } @@ -1767,7 +1729,7 @@ void theory_seq::add_reject2reject(expr* rej) { if (!aut) return; if (m_util.str.is_empty(s)) return; eautomaton::moves mvs; - aut->get_moves_to(src, mvs); + aut->get_moves_from(src, mvs); expr_ref head(m), tail(m), emp(m), conc(m); mk_decompose(s, emp, head, tail); literal rej1 = ctx.get_literal(rej); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index f19599472..59b721fa7 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -27,20 +27,10 @@ Revision History: #include "scoped_vector.h" #include "scoped_ptr_vector.h" #include "automaton.h" +#include "seq_rewriter.h" namespace smt { - typedef automaton eautomaton; - class re2automaton { - ast_manager& m; - seq_util u; - eautomaton* re2aut(expr* e); - eautomaton* seq2aut(expr* e); - public: - re2automaton(ast_manager& m); - eautomaton* operator()(expr* e) { return re2aut(e); } - }; - class theory_seq : public theory { typedef scoped_dependency_manager enode_pair_dependency_manager; typedef enode_pair_dependency_manager::dependency enode_pair_dependency; From 071a654a9a2c2d638813d94bd8c27e77f796fc28 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 27 Dec 2015 04:41:25 -0800 Subject: [PATCH 15/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 64 +++++ src/ast/rewriter/seq_rewriter.h | 1 + src/smt/smt_context.cpp | 1 - src/smt/theory_arith.h | 6 + src/smt/theory_arith_aux.h | 1 + src/smt/theory_arith_core.h | 37 ++- src/smt/theory_seq.cpp | 432 +++++++++++++++++------------- src/smt/theory_seq.h | 30 ++- 8 files changed, 360 insertions(+), 212 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 428c4b224..ca471baa1 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -693,17 +693,76 @@ br_status seq_rewriter::mk_str_to_regexp(expr* a, expr_ref& result) { return BR_FAILED; } br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) { + if (is_epsilon(a)) { + result = b; + return BR_DONE; + } + if (is_epsilon(b)) { + result = a; + return BR_DONE; + } return BR_FAILED; } +/* + (a + a) = a + (a + eps) = a + (eps + a) = a +*/ br_status seq_rewriter::mk_re_union(expr* a, expr* b, expr_ref& result) { + if (a == b) { + result = a; + return BR_DONE; + } + if (m_util.re.is_star(a) && is_epsilon(b)) { + result = a; + return BR_DONE; + } + if (m_util.re.is_star(b) && is_epsilon(a)) { + result = b; + return BR_DONE; + } return BR_FAILED; } +/* + a** = a* + (a* + b)* = (a + b)* + (a + b*)* = (a + b)* + (a*b*)* = (a + b)* +*/ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) { + expr* b, *c, *b1, *c1; + if (m_util.re.is_star(a)) { + result = a; + return BR_DONE; + } + if (m_util.re.is_union(a, b, c)) { + if (m_util.re.is_star(b, b1)) { + result = m_util.re.mk_star(m_util.re.mk_union(b1, c)); + return BR_REWRITE2; + } + if (m_util.re.is_star(c, c1)) { + result = m_util.re.mk_star(m_util.re.mk_union(b, c1)); + return BR_REWRITE2; + } + } + if (m_util.re.is_concat(a, b, c) && + m_util.re.is_star(b, b1) && m_util.re.is_star(c, c1)) { + result = m_util.re.mk_star(m_util.re.mk_union(b1, c1)); + return BR_REWRITE2; + } + return BR_FAILED; } + +/* + a+ = aa* +*/ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { return BR_FAILED; +// result = m_util.re.mk_concat(a, m_util.re.mk_star(a)); +// return BR_REWRITE2; } + br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) { sort* s; VERIFY(m_util.is_re(a, s)); @@ -1014,6 +1073,11 @@ bool seq_rewriter::length_constrained(unsigned szl, expr* const* l, unsigned szr return false; } +bool seq_rewriter::is_epsilon(expr* e) const { + expr* e1; + return m_util.re.is_to_re(e, e1) && m_util.str.is_empty(e1); +} + bool seq_rewriter::is_subsequence(unsigned szl, expr* const* l, unsigned szr, expr* const* r, expr_ref_vector& lhs, expr_ref_vector& rhs, bool& is_sat) { is_sat = true; diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index f6772bfe9..d5fac104b 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -75,6 +75,7 @@ class seq_rewriter { void add_next(u_map& next, unsigned idx, expr* cond); bool is_sequence(expr* e, expr_ref_vector& seq); + bool is_epsilon(expr* e) const; public: seq_rewriter(ast_manager & m, params_ref const & p = params_ref()): diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 738763c88..45dd7e3dc 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1405,7 +1405,6 @@ namespace smt { else if (d.is_theory_atom()) { theory * th = m_theories.get_plugin(d.get_theory()); SASSERT(th); - TRACE("seq", tout << d.get_theory() << "\n";); th->assign_eh(v, val == l_true); } else if (d.is_quantifier()) { diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 1e0c7c5f4..2f5590e25 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -1056,6 +1056,10 @@ namespace smt { // ----------------------------------- virtual bool get_value(enode * n, expr_ref & r); + bool get_lower(enode* n, expr_ref& r); + bool get_upper(enode* n, expr_ref& r); + bool to_expr(inf_numeral const& val, bool is_int, expr_ref& r); + // ----------------------------------- // @@ -1071,6 +1075,8 @@ namespace smt { unsigned num_eqs, enode_pair const * eqs, unsigned num_params, parameter* params); inf_eps_rational conflict_minimize(); + + private: virtual expr_ref mk_gt(theory_var v); diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index e2a2d48a6..2473b32ab 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -2157,6 +2157,7 @@ namespace smt { enode * r = n->get_root(); enode_vector::const_iterator it = r->begin_parents(); enode_vector::const_iterator end = r->end_parents(); + TRACE("shared", tout << get_context().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); for (; it != end; ++it) { enode * parent = *it; app * o = parent->get_owner(); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 96fe2d1f8..d6fe16089 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -3089,20 +3089,35 @@ namespace smt { // ----------------------------------- template - bool theory_arith::get_value(enode * n, expr_ref & r) { - theory_var v = n->get_th_var(get_id()); - if (v == null_theory_var) { - // TODO: generate fresh value different from other get_value(v) for all v. - return false; + bool theory_arith::to_expr(inf_numeral const& val, bool is_int, expr_ref & r) { + if (val.get_infinitesimal().is_zero()) { + numeral _val = val.get_rational(); + r = m_util.mk_numeral(_val.to_rational(), is_int); + return true; } - inf_numeral const & val = get_value(v); - if (!val.get_infinitesimal().is_zero()) { - // TODO: add support for infinitesimals + else { return false; } - numeral _val = val.get_rational(); - r = m_util.mk_numeral(_val.to_rational(), is_int(v)); - return true; + } + + template + bool theory_arith::get_value(enode * n, expr_ref & r) { + theory_var v = n->get_th_var(get_id()); + return v != null_theory_var && to_expr(get_value(v), is_int(v), r); + } + + template + bool theory_arith::get_lower(enode * n, expr_ref & r) { + theory_var v = n->get_th_var(get_id()); + bound* b = (v == null_theory_var) ? 0 : lower(v); + return b && to_expr(b->get_value(), is_int(v), r); + } + + template + bool theory_arith::get_upper(enode * n, expr_ref & r) { + theory_var v = n->get_th_var(get_id()); + bound* b = (v == null_theory_var) ? 0 : upper(v); + return b && to_expr(b->get_value(), is_int(v), r); } // ----------------------------------- diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 39ed71beb..d9e39c2ec 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -23,6 +23,7 @@ Revision History: #include "theory_seq.h" #include "seq_rewriter.h" #include "ast_trail.h" +#include "theory_arith.h" using namespace smt; @@ -37,6 +38,9 @@ struct display_expr { void theory_seq::solution_map::update(expr* e, expr* r, enode_pair_dependency* d) { + if (e == r) { + return; + } m_cache.reset(); std::pair value; if (m_map.find(e, value)) { @@ -61,6 +65,9 @@ expr* theory_seq::solution_map::find(expr* e, enode_pair_dependency*& d) { expr* result = e; while (m_map.find(result, value)) { d = m_dm.mk_join(d, value.second); + TRACE("seq", tout << mk_pp(result, m) << " -> " << mk_pp(value.first, m) << "\n";); + SASSERT(result != value.first); + SASSERT(e != value.first); result = value.first; } return result; @@ -139,8 +146,6 @@ theory_seq::theory_seq(ast_manager& m): m_axioms(m), m_axioms_head(0), m_branch_variable_head(0), - m_incomplete(false), - m_has_length(false), m_model_completion(false), m_mg(0), m_rewrite(m), @@ -179,37 +184,27 @@ final_check_status theory_seq::final_check_eh() { context & ctx = get_context(); TRACE("seq", display(tout);); if (!check_ineqs()) { + TRACE("seq", tout << "check_ineqs\n";); return FC_CONTINUE; } if (simplify_and_solve_eqs()) { + TRACE("seq", tout << "solve_eqs\n";); return FC_CONTINUE; } if (solve_nqs()) { - return FC_CONTINUE; - } - if (ctx.inconsistent()) { + TRACE("seq", tout << "solve_nqs\n";); return FC_CONTINUE; } if (branch_variable()) { TRACE("seq", tout << "branch\n";); return FC_CONTINUE; } - if (split_variable()) { - TRACE("seq", tout << "split_variable\n";); - return FC_CONTINUE; - } - if (ctx.inconsistent()) { - return FC_CONTINUE; - } if (!check_length_coherence()) { TRACE("seq", tout << "check_length_coherence\n";); return FC_CONTINUE; } - if (!check_length_coherence_tbd()) { - TRACE("seq", tout << "check_length_coherence\n";); - return FC_CONTINUE; - } if (propagate_automata()) { + TRACE("seq", tout << "propagate_automata\n";); return FC_CONTINUE; } if (is_solved()) { @@ -260,7 +255,7 @@ bool theory_seq::branch_variable() { return true; } } - return false; + return ctx.inconsistent(); } bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { @@ -315,33 +310,9 @@ bool theory_seq::assume_equality(expr* l, expr* r) { } } -bool theory_seq::split_variable() { - - return false; -} bool theory_seq::check_length_coherence() { - if (!m_has_length) return true; - context& ctx = get_context(); - bool coherent = true; - for (unsigned i = 0; i < m_eqs.size(); ++i) { - m_eqs[i].m_dep; - expr_ref v1(m), v2(m), l(m_eqs[i].m_lhs), r(m_eqs[i].m_rhs); - expr_ref len1(m_util.str.mk_length(l), m); - expr_ref len2(m_util.str.mk_length(r), m); - enode* n1 = ensure_enode(len1); - enode* n2 = ensure_enode(len2); - if (n1->get_root() != n2->get_root()) { - TRACE("seq", tout << len1 << " = " << len2 << "\n";); - propagate_eq(m_eqs[i].m_dep, n1, n2); - coherent = false; - } - } - return coherent; -} - -bool theory_seq::check_length_coherence_tbd() { - if (!m_has_length) return true; + if (m_length.empty()) return true; context& ctx = get_context(); bool coherent = true; // each variable that canonizes to itself can have length 0. @@ -359,9 +330,36 @@ bool theory_seq::check_length_coherence_tbd() { expr* f = m_rep.find(e, dep); if (is_var(f) && f == e) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); + expr_ref head(m), tail(m); + rational lo, hi; TRACE("seq", tout << "Unsolved " << mk_pp(e, m) << "\n";); - if (!assume_equality(e, emp)) { - expr_ref head(m), tail(m); + if (lower_bound(e, lo) && lo.is_pos() && lo < rational(512)) { + TRACE("seq", tout << "lower bound: " << mk_pp(e, m) << " " << lo << "\n";); + expr_ref low(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)), m); + expr_ref seq(e, m); + expr_ref_vector elems(m); + unsigned _lo = lo.get_unsigned(); + for (unsigned j = 0; j < _lo; ++j) { + mk_decompose(seq, emp, head, tail); + elems.push_back(head); + seq = tail; + } + elems.push_back(seq); + tail = m_util.str.mk_concat(elems.size(), elems.c_ptr()); + // len(e) >= low => e = tail + add_axiom(~mk_literal(low), mk_eq(e, tail, false)); + assume_equality(tail, e); + if (upper_bound(e, hi) && hi == lo) { + expr_ref high(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)), m); + add_axiom(~mk_literal(high), mk_eq(seq, emp, false)); + } + } + else if (upper_bound(e, hi) && hi.is_zero()) { + expr_ref len(m_util.str.mk_length(e), m); + expr_ref zero(m_autil.mk_int(0), m); + add_axiom(~mk_eq(len, zero, false), mk_eq(e, emp, false)); + } + else if (!assume_equality(e, emp)) { mk_decompose(e, emp, head, tail); // e = emp \/ e = unit(head.elem(e))*tail(e) expr_ref conc(m_util.str.mk_concat(head, tail), m); @@ -438,8 +436,6 @@ bool theory_seq::is_solved() { if (!m_automata[i]) return false; } - SASSERT(check_length_coherence()); - return true; } @@ -487,7 +483,7 @@ void theory_seq::propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2) -bool theory_seq::simplify_eq(expr* l, expr* r, enode_pair_dependency* deps) { +bool theory_seq::simplify_eq(expr* l, expr* r, enode_pair_dependency* deps, bool& propagated) { context& ctx = get_context(); seq_rewriter rw(m); expr_ref_vector lhs(m), rhs(m); @@ -497,44 +493,47 @@ bool theory_seq::simplify_eq(expr* l, expr* r, enode_pair_dependency* deps) { // equality is inconsistent. TRACE("seq", tout << lh << " != " << rh << "\n";); set_conflict(deps); + propagated = true; return true; } if (unchanged(l, lhs) && unchanged(r, rhs)) { return false; } + if (unchanged(r, lhs) && unchanged(l, rhs)) { + return false; + } SASSERT(lhs.size() == rhs.size()); for (unsigned i = 0; i < lhs.size(); ++i) { - expr_ref l(lhs[i].get(), m); - expr_ref r(rhs[i].get(), m); - if (m_util.is_seq(l) || m_util.is_re(l)) { - m_eqs.push_back(eq(l, r, deps)); + expr_ref li(lhs[i].get(), m); + expr_ref ri(rhs[i].get(), m); + if (m_util.is_seq(li) || m_util.is_re(li)) { + m_eqs.push_back(eq(li, ri, deps)); } else { - propagate_eq(deps, ensure_enode(l), ensure_enode(r)); + propagate_eq(deps, ensure_enode(li), ensure_enode(ri)); + propagated = true; } - } + } TRACE("seq", tout << mk_pp(l, m) << " = " << mk_pp(r, m) << " => "; - for (unsigned i = 0; i < m_eqs.size(); ++i) { - tout << m_eqs[i].m_lhs << " = " << m_eqs[i].m_rhs << "; "; + for (unsigned i = 0; i < lhs.size(); ++i) { + tout << mk_pp(lhs[i].get(), m) << " = " << mk_pp(rhs[i].get(), m) << "; "; } - tout << "\n"; - ); + tout << "\n";); return true; } -bool theory_seq::solve_unit_eq(expr* l, expr* r, enode_pair_dependency* deps) { +bool theory_seq::solve_unit_eq(expr* l, expr* r, enode_pair_dependency* deps, bool& propagated) { expr_ref lh = canonize(l, deps); expr_ref rh = canonize(r, deps); if (lh == rh) { return true; } if (is_var(lh) && !occurs(lh, rh)) { - add_solution(lh, rh, deps); - return true; + propagated = add_solution(lh, rh, deps) || propagated; } if (is_var(rh) && !occurs(rh, lh)) { - add_solution(rh, lh, deps); + propagated = add_solution(rh, lh, deps) || propagated; return true; } // Use instead reference counts for dependencies to GC? @@ -585,25 +584,32 @@ bool theory_seq::is_head_elem(expr* e) const { return is_skolem(m_head_elem, e); } -void theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { +bool theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { + if (l == r) { + return false; + } context& ctx = get_context(); m_rep.update(l, r, deps); // TBD: skip new equalities for non-internalized terms. - if (ctx.e_internalized(l) && ctx.e_internalized(r)) { + if (ctx.e_internalized(l) && ctx.e_internalized(r) && ctx.get_enode(l)->get_root() != ctx.get_enode(r)->get_root()) { propagate_eq(deps, ctx.get_enode(l), ctx.get_enode(r)); + return true; + } + else { + return false; } } -bool theory_seq::pre_process_eqs(bool simplify_or_solve) { +bool theory_seq::pre_process_eqs(bool simplify_or_solve, bool& propagated) { context& ctx = get_context(); bool change = false; for (unsigned i = 0; !ctx.inconsistent() && i < m_eqs.size(); ++i) { eq e = m_eqs[i]; if (simplify_or_solve? - simplify_eq(e.m_lhs, e.m_rhs, e.m_dep): - solve_unit_eq(e.m_lhs, e.m_rhs, e.m_dep)) { + simplify_eq(e.m_lhs, e.m_rhs, e.m_dep, propagated): + solve_unit_eq(e.m_lhs, e.m_rhs, e.m_dep, propagated)) { if (i + 1 != m_eqs.size()) { eq e1 = m_eqs[m_eqs.size()-1]; m_eqs.set(i, e1); @@ -625,7 +631,7 @@ bool theory_seq::solve_nqs() { change = solve_ne(i) || change; } } - return change; + return change || ctx.inconsistent(); } bool theory_seq::solve_ne(unsigned idx) { @@ -728,12 +734,12 @@ void theory_seq::erase_index(unsigned idx, unsigned i) { bool theory_seq::simplify_and_solve_eqs() { context & ctx = get_context(); - bool change = simplify_eqs(); - while (!ctx.inconsistent() && solve_basic_eqs()) { - simplify_eqs(); - change = true; + bool propagated = false; + simplify_eqs(propagated); + while (!ctx.inconsistent() && solve_basic_eqs(propagated)) { + simplify_eqs(propagated); } - return change; + return propagated || ctx.inconsistent(); } @@ -741,8 +747,9 @@ bool theory_seq::internalize_term(app* term) { TRACE("seq", tout << mk_pp(term, m) << "\n";); context & ctx = get_context(); unsigned num_args = term->get_num_args(); + expr* arg; for (unsigned i = 0; i < num_args; i++) { - expr* arg = term->get_arg(i); + arg = term->get_arg(i); mk_var(ensure_enode(arg)); } if (m.is_bool(term)) { @@ -761,23 +768,35 @@ bool theory_seq::internalize_term(app* term) { } mk_var(e); } - if (m_util.str.is_length(term) && !m_has_length) { - m_trail_stack.push(value_trail(m_has_length)); - m_has_length = true; - } - if (!m_util.str.is_concat(term) && - !m_util.str.is_string(term) && - !m_util.str.is_empty(term) && - !m_util.str.is_unit(term) && - !m_util.str.is_suffix(term) && - !m_util.str.is_prefix(term) && - !m_util.str.is_contains(term) && - !m_util.is_skolem(term)) { - set_incomplete(term); + if (m_util.str.is_length(term, arg) && !has_length(arg)) { + add_length(arg); } return true; } +void theory_seq::add_length(expr* e) { + SASSERT(!has_length(e)); + m_length.insert(e); + m_trail_stack.push(insert_obj_trail(m_length, e)); +} + +/* + ensure that all elements in equivalence class occur under an applicatin of 'length' +*/ +void theory_seq::enforce_length(enode* n) { + enode* n1 = n; + do { + expr* o = n->get_owner(); + if (!has_length(o)) { + expr_ref len(m_util.str.mk_length(o), m); + enque_axiom(len); + add_length(o); + } + n = n->get_next(); + } + while (n1 != n); +} + void theory_seq::apply_sort_cnstr(enode* n, sort* s) { mk_var(n); } @@ -886,14 +905,6 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { -void theory_seq::set_incomplete(app* term) { - if (!m_incomplete) { - TRACE("seq", tout << "Incomplete operator: " << mk_pp(term, m) << "\n";); - m_trail_stack.push(value_trail(m_incomplete)); - m_incomplete = true; - } -} - theory_var theory_seq::mk_var(enode* n) { if (!m_util.is_seq(n->get_owner()) && !m_util.is_re(n->get_owner())) { @@ -927,7 +938,6 @@ expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { if (m_rep.find_cache(e0, ed)) { eqs = m_dm.mk_join(eqs, ed.second); result = ed.first; - TRACE("seq", tout << mk_pp(e0, m) << " |-> " << result << " "; display_deps(tout, eqs);); return result; } expr* e = m_rep.find(e0, deps); @@ -999,7 +1009,7 @@ expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { expr_dep edr(result, deps); m_rep.add_cache(e0, edr); eqs = m_dm.mk_join(eqs, deps); - TRACE("seq", tout << mk_pp(e0, m) << " |--> " << result << "\n"; + TRACE("seq_verbose", tout << mk_pp(e0, m) << " |--> " << result << "\n"; display_deps(tout, eqs);); return result; } @@ -1043,21 +1053,8 @@ void theory_seq::deque_axiom(expr* n) { else if (m_util.str.is_at(n)) { add_at_axiom(n); } - else if (m_util.str.is_unit(n)) { - add_length_unit_axiom(n); - } - else if (m_util.str.is_empty(n)) { - add_length_empty_axiom(n); - } - else if (m_util.str.is_concat(n)) { - add_length_concat_axiom(n); - } else if (m_util.str.is_string(n)) { add_elim_string_axiom(n); - // add_length_string_axiom(n); - } - else if (m_util.str.is_in_re(n)) { - add_in_re_axiom(n); } } @@ -1180,24 +1177,12 @@ void theory_seq::add_replace_axiom(expr* r) { tightest_prefix(s, x, ~cnt); } -void theory_seq::add_length_unit_axiom(expr* n) { - if (!m_has_length) return; - SASSERT(m_util.str.is_unit(n)); - expr_ref one(m_autil.mk_int(1), m), len(m_util.str.mk_length(n), m); - add_axiom(mk_eq(len, one, false)); -} - -void theory_seq::add_length_empty_axiom(expr* n) { - if (!m_has_length) return; - SASSERT(m_util.str.is_empty(n)); - expr_ref zero(m_autil.mk_int(0), m), len(m_util.str.mk_length(n), m); - add_axiom(mk_eq(len, zero, false)); -} - void theory_seq::add_elim_string_axiom(expr* n) { zstring s; VERIFY(m_util.str.is_string(n, s)); - SASSERT(s.length() > 0); + if (s.length() == 0) { + return; + } expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, s.length()-1)), m); for (unsigned i = s.length()-1; i > 0; ) { --i; @@ -1207,25 +1192,14 @@ void theory_seq::add_elim_string_axiom(expr* n) { m_rep.update(n, result, 0); } -void theory_seq::add_length_string_axiom(expr* n) { - if (!m_has_length) return; - zstring s; - VERIFY(m_util.str.is_string(n, s)); - expr_ref len(m_util.str.mk_length(n), m); - expr_ref ls(m_autil.mk_numeral(rational(s.length(), rational::ui64()), true), m); - add_axiom(mk_eq(len, ls, false)); -} -void theory_seq::add_length_concat_axiom(expr* n) { - if (!m_has_length) return; - expr* a, *b; - VERIFY(m_util.str.is_concat(n, a, b)); - expr_ref len(m_util.str.mk_length(n), m); - expr_ref _a(m_util.str.mk_length(a), m); - expr_ref _b(m_util.str.mk_length(b), m); - expr_ref ab(m_autil.mk_add(_a, _b), m); - m_rewrite(ab); - add_axiom(mk_eq(ab, len, false)); +void theory_seq::add_length_coherence_axiom(expr* n) { + expr_ref len(n, m); + m_rewrite(len); + if (n != len) { + TRACE("seq", tout << "Add length coherence for " << mk_pp(n, m) << "\n";); + add_axiom(mk_eq(n, len, false)); + } } /* @@ -1240,8 +1214,7 @@ void theory_seq::add_length_axiom(expr* n) { VERIFY(m_util.str.is_length(n, x)); if (!m_util.str.is_unit(x) && !m_util.str.is_empty(x) && - !m_util.str.is_string(x) && - !m_util.str.is_concat(x)) { + !m_util.str.is_string(x)) { expr_ref zero(m_autil.mk_int(0), m); expr_ref emp(m_util.str.mk_empty(m.get_sort(x)), m); literal eq1(mk_eq(zero, n, false)); @@ -1250,18 +1223,45 @@ void theory_seq::add_length_axiom(expr* n) { add_axiom(~eq1, eq2); add_axiom(~eq2, eq1); } + if (m_util.str.is_concat(x) || + m_util.str.is_unit(x) || + m_util.str.is_empty(x) || + m_util.str.is_string(x)) { + add_length_coherence_axiom(x); + } } -// -// the empty sequence is accepted only in the final states. -// membership holds iff the initial state holds. -// -void theory_seq::add_in_re_axiom(expr* n) { + + +void theory_seq::propagate_in_re(expr* n, bool is_true) { + TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); expr* e1, *e2; VERIFY(m_util.str.is_in_re(n, e1, e2)); + + expr_ref tmp(n, m); + m_rewrite(tmp); + if (m.is_true(tmp)) { + if (!is_true) { + literal_vector lits; + lits.push_back(mk_literal(n)); + set_conflict(0, lits); + } + return; + } + else if (m.is_false(tmp)) { + if (is_true) { + literal_vector lits; + lits.push_back(~mk_literal(n)); + set_conflict(0, lits); + } + return; + } + eautomaton* a = get_automaton(e2); if (!a) return; - + if (m_util.str.is_empty(e1)) return; + context& ctx = get_context(); + expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); for (unsigned i = 0; i < a->num_states(); ++i) { literal acc = mk_accept(emp, e2, i); @@ -1269,17 +1269,7 @@ void theory_seq::add_in_re_axiom(expr* n) { add_axiom(a->is_final_state(i)?acc:~acc); add_axiom(a->is_final_state(i)?~rej:rej); } -} - -void theory_seq::propagate_in_re(expr* n, bool is_true) { - TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); - expr* e1, *e2; - VERIFY(m_util.str.is_in_re(n, e1, e2)); - eautomaton* a = get_automaton(e2); - if (!a) return; - if (m_util.str.is_empty(e1)) return; - context& ctx = get_context(); unsigned_vector states; a->get_epsilon_closure(a->init(), states); literal_vector lits; @@ -1323,6 +1313,80 @@ enode* theory_seq::ensure_enode(expr* e) { return ctx.get_enode(e); } +static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { + ast_manager& m = ctx.get_manager(); + theory* th = ctx.get_theory(afid); + if (th && ctx.e_internalized(e)) { + return dynamic_cast(th); + } + else { + return 0; + } +} + +bool theory_seq::lower_bound(expr* _e, rational& lo) { + context& ctx = get_context(); + expr_ref e(m_util.str.mk_length(_e), m); + theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + expr_ref _lo(m); + if (!tha || !tha->get_lower(ctx.get_enode(e), _lo)) return false; + return m_autil.is_numeral(_lo, lo) && lo.is_int(); +} + +bool theory_seq::upper_bound(expr* _e, rational& hi) { + context& ctx = get_context(); + expr_ref e(m_util.str.mk_length(_e), m); + theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + expr_ref _hi(m); + if (!tha || !tha->get_upper(ctx.get_enode(e), _hi)) return false; + return m_autil.is_numeral(_hi, hi) && hi.is_int(); +} + +bool theory_seq::get_length(expr* e, rational& val) { + context& ctx = get_context(); + theory* th = ctx.get_theory(m_autil.get_family_id()); + if (!th) return false; + theory_mi_arith* tha = dynamic_cast(th); + if (!tha) return false; + rational val1; + expr_ref len(m), len_val(m); + expr* e1, *e2; + ptr_vector todo; + todo.push_back(e); + val.reset(); + zstring s; + while (!todo.empty()) { + expr* c = todo.back(); + todo.pop_back(); + if (m_util.str.is_concat(c, e1, e2)) { + todo.push_back(e1); + todo.push_back(e2); + } + else if (m_util.str.is_unit(c)) { + val += rational(1); + } + else if (m_util.str.is_empty(c)) { + continue; + } + else if (m_util.str.is_string(c, s)) { + val += rational(s.length()); + } + else { + len = m_util.str.mk_length(c); + if (ctx.e_internalized(len) && + tha->get_value(ctx.get_enode(len), len_val) && + m_autil.is_numeral(len_val, val1)) { + val += val1; + } + else { + TRACE("seq", tout << "No length provided for " << len << "\n";); + return false; + } + } + } + return val.is_int(); +} + /* TBD: check semantics of extract. @@ -1445,7 +1509,7 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); TRACE("seq", - tout << mk_pp(ctx.bool_var2enode(v)->get_owner(), m) << " => " + tout << mk_pp(ctx.bool_var2expr(v), m) << " => " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); literal lit(v); justification* js = @@ -1522,7 +1586,17 @@ void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { expr_ref o1(n1->get_owner(), m); expr_ref o2(n2->get_owner(), m); TRACE("seq", tout << o1 << " = " << o2 << "\n";); - m_eqs.push_back(eq(o1, o2, m_dm.mk_leaf(enode_pair(n1, n2)))); + enode_pair_dependency* deps = m_dm.mk_leaf(enode_pair(n1, n2)); + bool propagated = false; + if (!simplify_eq(o1, o2, deps, propagated)) { + m_eqs.push_back(eq(o1, o2, deps)); + } + if (has_length(o1) && !has_length(o2)) { + enforce_length(n2); + } + else if (has_length(o2) && !has_length(o1)) { + enforce_length(n1); + } } } @@ -1531,8 +1605,12 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { enode* n2 = get_enode(v2); expr_ref e1(n1->get_owner(), m); expr_ref e2(n2->get_owner(), m); - m_nqs.push_back(ne(e1, e2)); m_exclude.update(e1, e2); + expr_ref eq(m.mk_eq(e1, e2), m); + m_rewrite(eq); + if (!m.is_false(eq)) { + m_nqs.push_back(ne(e1, e2)); + } } void theory_seq::push_scope_eh() { @@ -1565,34 +1643,10 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_replace(n) || m_util.str.is_extract(n) || m_util.str.is_at(n) || - m_util.str.is_concat(n) || - m_util.str.is_empty(n) || - m_util.str.is_unit(n) || m_util.str.is_string(n) || - m_util.str.is_in_re(n) || is_step(n)) { enque_axiom(n); } -#if 0 - if (m_util.str.is_in_re(n) || - m_util.str.is_contains(n) || - m_util.str.is_suffix(n) || - m_util.str.is_prefix(n)) { - context& ctx = get_context(); - TRACE("seq", tout << mk_pp(n, m) << "\n";); - bool_var bv = ctx.get_bool_var(n); - switch (ctx.get_assignment(bv)) { - case l_false: - assign_eh(bv, false); - break; - case l_true: - assign_eh(bv, true); - break; - case l_undef: - break; - } - } -#endif } @@ -1687,7 +1741,9 @@ void theory_seq::add_accept2step(expr* acc) { mk_decompose(s, emp, head, tail); literal_vector lits; lits.push_back(~ctx.get_literal(acc)); - lits.push_back(mk_eq(emp, s, false)); + if (aut->is_final_state(src)) { + lits.push_back(mk_eq(emp, s, false)); + } for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move mv = mvs[i]; step = mk_step(s, tail, re, src, mv.dst(), mv.t()); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 59b721fa7..e7f610695 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -259,7 +259,7 @@ namespace smt { unsigned m_axioms_head; // index of first axiom to add. unsigned m_branch_variable_head; // index of first equation to examine. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. - bool m_has_length; // is length applied + obj_hashtable m_length; // is length applied bool m_model_completion; // during model construction, invent values in canonizer model_generator* m_mg; th_rewriter m_rewrite; @@ -306,14 +306,13 @@ namespace smt { bool split_variable(); // split a variable bool is_solved(); bool check_length_coherence(); - bool check_length_coherence_tbd(); bool check_ineq_coherence(); - bool pre_process_eqs(bool simplify_or_solve); - bool simplify_eqs() { return pre_process_eqs(true); } - bool solve_basic_eqs() { return pre_process_eqs(false); } - bool simplify_eq(expr* l, expr* r, enode_pair_dependency* dep); - bool solve_unit_eq(expr* l, expr* r, enode_pair_dependency* dep); + bool pre_process_eqs(bool simplify_or_solve, bool& propagated); + bool simplify_eqs(bool& propagated) { return pre_process_eqs(true, propagated); } + bool solve_basic_eqs(bool& propagated) { return pre_process_eqs(false, propagated); } + bool simplify_eq(expr* l, expr* r, enode_pair_dependency* dep, bool& propagated); + bool solve_unit_eq(expr* l, expr* r, enode_pair_dependency* dep, bool& propagated); bool solve_nqs(); bool solve_ne(unsigned i); @@ -332,7 +331,7 @@ namespace smt { // variable solving utilities bool occurs(expr* a, expr* b); bool is_var(expr* b); - void add_solution(expr* l, expr* r, enode_pair_dependency* dep); + bool add_solution(expr* l, expr* r, enode_pair_dependency* dep); bool is_left_select(expr* a, expr*& b); bool is_right_select(expr* a, expr*& b); bool is_head_elem(expr* a) const; @@ -349,10 +348,12 @@ namespace smt { void add_replace_axiom(expr* e); void add_extract_axiom(expr* e); void add_length_axiom(expr* n); - void add_length_unit_axiom(expr* n); - void add_length_empty_axiom(expr* n); - void add_length_concat_axiom(expr* n); - void add_length_string_axiom(expr* n); + void add_length_coherence_axiom(expr* n); + + bool has_length(expr *e) const { return m_length.contains(e); } + void add_length(expr* e); + void enforce_length(enode* n); + void add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); @@ -361,6 +362,11 @@ namespace smt { expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); + // arithmetic integration + bool lower_bound(expr* s, rational& lo); + bool upper_bound(expr* s, rational& hi); + bool get_length(expr* s, rational& val); + void mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); bool is_skolem(symbol const& s, expr* e) const; From 739043e2733414bc46f744ba5984daf7dbc19965 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Dec 2015 10:28:43 -0800 Subject: [PATCH 16/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 13 +- src/math/automata/automaton.h | 20 +- src/smt/theory_seq.cpp | 325 +++++++++++++++++------------- src/smt/theory_seq.h | 12 +- 4 files changed, 213 insertions(+), 157 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index ca471baa1..aa27489ad 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -25,6 +25,14 @@ Notes: #include"automaton.h" +struct display_expr1 { + ast_manager& m; + display_expr1(ast_manager& m): m(m) {} + std::ostream& display(std::ostream& out, expr* e) const { + return out << mk_pp(e, m); + } +}; + re2automaton::re2automaton(ast_manager& m): m(m), u(m) {} @@ -36,6 +44,7 @@ eautomaton* re2automaton::operator()(expr* e) { return r; } + eautomaton* re2automaton::re2aut(expr* e) { SASSERT(u.is_re(e)); expr* e1, *e2; @@ -230,8 +239,8 @@ br_status seq_rewriter::mk_seq_concat(expr* a, expr* b, expr_ref& result) { result = m_util.str.mk_string(s1 + s2); return BR_DONE; } - if (m_util.str.is_concat(b, c, d)) { - result = m_util.str.mk_concat(m_util.str.mk_concat(a, c), d); + if (m_util.str.is_concat(a, c, d)) { + result = m_util.str.mk_concat(c, m_util.str.mk_concat(d, b)); return BR_REWRITE2; } if (m_util.str.is_empty(a)) { diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index dabfa1417..fb6bb31fd 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -84,6 +84,12 @@ private: mutable uint_set m_visited; mutable unsigned_vector m_todo; + struct default_display { + std::ostream& display(std::ostream& out, T* t) { + return out << t; + } + }; + public: // The empty automaton: @@ -216,7 +222,7 @@ public: mvs.push_back(move(m, 0, a.init() + offset1)); append_moves(offset1, a, mvs); for (unsigned i = 0; i < a.m_final_states.size(); ++i) { - mvs.push_back(move(m, a.m_final_states[i], b.init())); + mvs.push_back(move(m, a.m_final_states[i], b.init() + offset2)); } append_moves(offset2, b, mvs); append_final(offset2, b, final); @@ -278,7 +284,7 @@ public: for (unsigned j = 0; found && j < mvs.size(); ++j) { found = (mvs[j].dst() == m_init) && mvs[j].is_epsilon(); } - if (!found) { + if (!found && state != m_init) { add(move(m, state, m_init)); } } @@ -301,7 +307,7 @@ public: if (src == dst) { // just remove this edge. } - else if (1 == in_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) { + else if (1 == in_degree(src) && 1 == out_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) { move const& mv0 = m_delta_inv[src][0]; unsigned src0 = mv0.src(); T* t = mv0.t(); @@ -311,8 +317,9 @@ public: } add(move(m, src0, dst, t)); remove(src0, src, t); + } - else if (1 == out_degree(dst) && init() != dst && (!is_final_state(dst) || is_final_state(src))) { + else if (1 == out_degree(dst) && 1 == in_degree(dst) && init() != dst && (!is_final_state(dst) || is_final_state(src))) { move const& mv1 = m_delta[dst][0]; unsigned dst1 = mv1.dst(); T* t = mv1.t(); @@ -322,6 +329,7 @@ public: } add(move(m, src, dst1, t)); remove(dst, dst1, t); + } else { continue; @@ -422,8 +430,8 @@ public: get_moves(state, m_delta_inv, mvs, epsilon_closure); } - template - std::ostream& display(std::ostream& out, D& displayer) const { + template + std::ostream& display(std::ostream& out, D& displayer = D()) const { out << "init: " << init() << "\n"; out << "final: "; for (unsigned i = 0; i < m_final_states.size(); ++i) out << m_final_states[i] << " "; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index d9e39c2ec..983730158 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -146,7 +146,6 @@ theory_seq::theory_seq(ast_manager& m): m_axioms(m), m_axioms_head(0), m_branch_variable_head(0), - m_model_completion(false), m_mg(0), m_rewrite(m), m_util(m), @@ -157,14 +156,12 @@ theory_seq::theory_seq(ast_manager& m): m_steps_qhead(0) { m_prefix = "seq.prefix.suffix"; m_suffix = "seq.suffix.prefix"; - m_left = "seq.left"; - m_right = "seq.right"; m_contains_left = "seq.contains.left"; m_contains_right = "seq.contains.right"; m_accept = "aut.accept"; m_reject = "aut.reject"; m_tail = "seq.tail"; - m_head_elem = "seq.head.elem"; + m_nth = "seq.nth"; m_seq_first = "seq.first"; m_seq_last = "seq.last"; m_indexof_left = "seq.indexof.left"; @@ -196,7 +193,7 @@ final_check_status theory_seq::final_check_eh() { return FC_CONTINUE; } if (branch_variable()) { - TRACE("seq", tout << "branch\n";); + TRACE("seq", tout << "branch_variable\n";); return FC_CONTINUE; } if (!check_length_coherence()) { @@ -315,27 +312,27 @@ bool theory_seq::check_length_coherence() { if (m_length.empty()) return true; context& ctx = get_context(); bool coherent = true; - // each variable that canonizes to itself can have length 0. - unsigned sz = get_num_vars(); - for (unsigned i = 0; i < sz; ++i) { - unsigned j = (i + m_branch_variable_head) % sz; - enode* n = get_enode(j); - expr* e = n->get_owner(); - if (m_util.is_re(e)) { - continue; - } - SASSERT(m_util.is_seq(e)); - // extend length of variables. + obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); + for (; it != end; ++it) { + expr* e = *it; enode_pair_dependency* dep = 0; expr* f = m_rep.find(e, dep); if (is_var(f) && f == e) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); expr_ref head(m), tail(m); rational lo, hi; - TRACE("seq", tout << "Unsolved " << mk_pp(e, m) << "\n";); + TRACE("seq", tout << "Unsolved " << mk_pp(e, m); + if (!lower_bound(e, lo)) lo = -rational::one(); + if (!upper_bound(e, hi)) hi = -rational::one(); + tout << " lo: " << lo << " "; + tout << "hi: " << hi << " "; + tout << "\n"; + ctx.display(tout); + ); + + if (lower_bound(e, lo) && lo.is_pos() && lo < rational(512)) { - TRACE("seq", tout << "lower bound: " << mk_pp(e, m) << " " << lo << "\n";); - expr_ref low(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)), m); + literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); expr_ref seq(e, m); expr_ref_vector elems(m); unsigned _lo = lo.get_unsigned(); @@ -347,18 +344,14 @@ bool theory_seq::check_length_coherence() { elems.push_back(seq); tail = m_util.str.mk_concat(elems.size(), elems.c_ptr()); // len(e) >= low => e = tail - add_axiom(~mk_literal(low), mk_eq(e, tail, false)); + add_axiom(~low, mk_eq(e, tail, false)); assume_equality(tail, e); - if (upper_bound(e, hi) && hi == lo) { - expr_ref high(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)), m); - add_axiom(~mk_literal(high), mk_eq(seq, emp, false)); + if (upper_bound(e, hi)) { + expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); + expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); + add_axiom(~mk_literal(high1), mk_literal(high2)); } } - else if (upper_bound(e, hi) && hi.is_zero()) { - expr_ref len(m_util.str.mk_length(e), m); - expr_ref zero(m_autil.mk_int(0), m); - add_axiom(~mk_eq(len, zero, false), mk_eq(e, emp, false)); - } else if (!assume_equality(e, emp)) { mk_decompose(e, emp, head, tail); // e = emp \/ e = unit(head.elem(e))*tail(e) @@ -366,7 +359,6 @@ bool theory_seq::check_length_coherence() { add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); assume_equality(tail, emp); } - m_branch_variable_head = j + 1; return false; } } @@ -380,8 +372,8 @@ void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); emp = m_util.str.mk_empty(m.get_sort(e)); if (m_util.str.is_empty(e)) { - head = m_util.str.mk_unit(mk_skolem(m_head_elem, e, 0, 0, char_sort)); - tail = mk_skolem(m_tail, e); + head = m_util.str.mk_unit(mk_skolem(m_nth, e, m_autil.mk_int(0), 0, char_sort)); + tail = e; } else if (m_util.str.is_string(e, s)) { head = m_util.str.mk_unit(m_util.str.mk_char(s, 0)); @@ -395,13 +387,18 @@ void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& head = e1; tail = e2; } + else if (is_skolem(m_tail, e)) { + rational r; + app* a = to_app(e); + expr* s = a->get_arg(0); + VERIFY (m_autil.is_numeral(a->get_arg(1), r)); + expr* idx = m_autil.mk_int(r.get_unsigned() + 1); + head = m_util.str.mk_unit(mk_skolem(m_nth, s, idx, 0, char_sort)); + tail = mk_skolem(m_tail, s, idx); + } else { - head = m_util.str.mk_unit(mk_skolem(m_head_elem, e, 0, 0, char_sort)); - tail = mk_skolem(m_tail, e); - if (!m_util.is_skolem(e)) { - expr_ref conc(m_util.str.mk_concat(head, tail), m); - add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); - } + head = m_util.str.mk_unit(mk_skolem(m_nth, e, m_autil.mk_int(0), 0, char_sort)); + tail = mk_skolem(m_tail, e, m_autil.mk_int(0)); } } @@ -531,6 +528,7 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, enode_pair_dependency* deps, bo } if (is_var(lh) && !occurs(lh, rh)) { propagated = add_solution(lh, rh, deps) || propagated; + return true; } if (is_var(rh) && !occurs(rh, lh)) { propagated = add_solution(rh, lh, deps) || propagated; @@ -547,19 +545,21 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, enode_pair_dependency* deps, bo bool theory_seq::occurs(expr* a, expr* b) { // true if a occurs under an interpreted function or under left/right selector. SASSERT(is_var(a)); + SASSERT(m_todo.empty()); expr* e1, *e2; - while (is_left_select(a, e1) || is_right_select(a, e1)) { - a = e1; + m_todo.push_back(b); + while (!m_todo.empty()) { + b = m_todo.back(); + if (a == b) { + m_todo.reset(); + return true; + } + m_todo.pop_back(); + if (m_util.str.is_concat(b, e1, e2)) { + m_todo.push_back(e1); + m_todo.push_back(e2); + } } - if (m_util.str.is_concat(b, e1, e2)) { - return occurs(a, e1) || occurs(a, e2); - } - while (is_left_select(b, e1) || is_right_select(b, e1)) { - b = e1; - } - if (a == b) { - return true; - } return false; } @@ -572,16 +572,9 @@ bool theory_seq::is_var(expr* a) { !m_util.str.is_unit(a); } -bool theory_seq::is_left_select(expr* a, expr*& b) { - return is_skolem(m_left, a) && (b = to_app(a)->get_arg(0), true); -} - -bool theory_seq::is_right_select(expr* a, expr*& b) { - return is_skolem(m_right, a) && (b = to_app(a)->get_arg(0), true); -} bool theory_seq::is_head_elem(expr* e) const { - return is_skolem(m_head_elem, e); + return is_skolem(m_nth, e); } bool theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { @@ -589,6 +582,7 @@ bool theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { return false; } context& ctx = get_context(); + TRACE("seq", tout << mk_pp(l, m) << " ==> " << mk_pp(r, m) << "\n";); m_rep.update(l, r, deps); // TBD: skip new equalities for non-internalized terms. if (ctx.e_internalized(l) && ctx.e_internalized(r) && ctx.get_enode(l)->get_root() != ctx.get_enode(r)->get_root()) { @@ -768,9 +762,6 @@ bool theory_seq::internalize_term(app* term) { } mk_var(e); } - if (m_util.str.is_length(term, arg) && !has_length(arg)) { - add_length(arg); - } return true; } @@ -889,20 +880,103 @@ void theory_seq::init_model(model_generator & mg) { mg.register_factory(m_factory); } + +class seq_value_proc : public model_value_proc { + theory_seq& th; + app* n; + svector m_dependencies; +public: + seq_value_proc(theory_seq& th, app* n): th(th), n(n) { + } + virtual ~seq_value_proc() {} + void add_dependency(enode* n) { m_dependencies.push_back(model_value_dependency(n)); } + virtual void get_dependencies(buffer & result) { + result.append(m_dependencies.size(), m_dependencies.c_ptr()); + } + virtual app * mk_value(model_generator & mg, ptr_vector & values) { + SASSERT(values.size() == m_dependencies.size()); + ast_manager& m = mg.get_manager(); + if (values.empty()) { + return th.mk_value(n); + } + SASSERT(values.size() == n->get_num_args()); + return th.mk_value(mg.get_manager().mk_app(n->get_decl(), values.size(), values.c_ptr())); + } +}; + + model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { - enode_pair_dependency* deps = 0; - expr_ref e(n->get_owner(), m); - flet _model_completion(m_model_completion, true); - m_rep.reset_cache(); - m_mg = &mg; - e = canonize(e, deps); - m_mg = 0; - SASSERT(is_app(e)); - TRACE("seq", tout << mk_pp(n->get_owner(), m) << " -> " << e << "\n";); - m_factory->add_trail(e); - return alloc(expr_wrapper_proc, to_app(e)); + context& ctx = get_context(); + enode_pair_dependency* dep = 0; + expr* e = m_rep.find(n->get_owner(), dep); + expr* e1, *e2; + seq_value_proc* sv = alloc(seq_value_proc, *this, to_app(e)); + if (m_util.str.is_concat(e, e1, e2)) { + sv->add_dependency(ctx.get_enode(e1)); + sv->add_dependency(ctx.get_enode(e2)); + } + else if (m_util.str.is_unit(e, e1)) { + sv->add_dependency(ctx.get_enode(e1)); + } + return sv; } +app* theory_seq::mk_value(app* e) { + expr* e1; + expr_ref result(e, m); + if (m_util.str.is_unit(e, e1)) { + enode_pair_dependency* deps = 0; + result = expand(e1, deps); + bv_util bv(m); + rational val; + unsigned sz; + if (bv.is_numeral(result, val, sz) && sz == zstring().num_bits()) { + svector val_as_bits; + for (unsigned i = 0; i < sz; ++i) { + val_as_bits.push_back(!val.is_even()); + val = div(val, rational(2)); + } + result = m_util.str.mk_string(zstring(sz, val_as_bits.c_ptr())); + } + else { + result = m_util.str.mk_unit(result); + } + } + else if (is_var(e)) { + SASSERT(m_factory); + expr_ref val(m); + val = m_factory->get_some_value(m.get_sort(e)); + if (val) { + result = val; + } + else { + result = e; + } + } + else if (is_head_elem(e)) { + enode* n = get_context().get_enode(e)->get_root(); + enode* n0 = n; + bool found_value = false; + do { + result = n->get_owner(); + found_value = m.is_model_value(result); + } + while (n0 != n && !found_value); + + if (!found_value) { + if (m_util.is_char(result)) { + result = m_util.str.mk_char('#'); + } + else { + result = m_mg->get_some_value(m.get_sort(result)); + } + } + } + m_rewrite(result); + m_factory->add_trail(result); + TRACE("seq", tout << mk_pp(e, m) << " -> " << result << "\n";); + return to_app(result); +} theory_var theory_seq::mk_var(enode* n) { @@ -957,49 +1031,6 @@ expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { else if (m_util.str.is_contains(e, e1, e2)) { result = m_util.str.mk_contains(expand(e1, deps), expand(e2, deps)); } - else if (m_model_completion && is_var(e)) { - SASSERT(m_factory); - expr_ref val(m); - val = m_factory->get_some_value(m.get_sort(e)); - if (val) { - m_rep.update(e, val, 0); - result = val; - } - else { - result = e; - } - } - else if (m_model_completion && m_util.str.is_unit(e, e1)) { - result = expand(e1, deps); - bv_util bv(m); - rational val; - unsigned sz; - if (bv.is_numeral(result, val, sz) && sz == zstring().num_bits()) { - svector val_as_bits; - for (unsigned i = 0; i < sz; ++i) { - val_as_bits.push_back(!val.is_even()); - val = div(val, rational(2)); - } - result = m_util.str.mk_string(zstring(sz, val_as_bits.c_ptr())); - } - else { - result = m_util.str.mk_unit(result); - } - } - else if (m_model_completion && is_head_elem(e)) { - enode* n = get_context().get_enode(e)->get_root(); - result = n->get_owner(); - if (!m.is_model_value(result)) { - if (m_util.is_char(result)) { - result = m_util.str.mk_char('#'); - } - else { - result = m_mg->get_some_value(m.get_sort(result)); - } - } - m_rep.update(e, result, 0); - TRACE("seq", tout << mk_pp(e, m) << " |-> " << result << "\n";); - } else { result = e; } @@ -1193,15 +1224,6 @@ void theory_seq::add_elim_string_axiom(expr* n) { } -void theory_seq::add_length_coherence_axiom(expr* n) { - expr_ref len(n, m); - m_rewrite(len); - if (n != len) { - TRACE("seq", tout << "Add length coherence for " << mk_pp(n, m) << "\n";); - add_axiom(mk_eq(n, len, false)); - } -} - /* let n = len(x) @@ -1212,9 +1234,18 @@ void theory_seq::add_length_coherence_axiom(expr* n) { void theory_seq::add_length_axiom(expr* n) { expr* x; VERIFY(m_util.str.is_length(n, x)); - if (!m_util.str.is_unit(x) && - !m_util.str.is_empty(x) && - !m_util.str.is_string(x)) { + if (m_util.str.is_concat(x) || + m_util.str.is_unit(x) || + m_util.str.is_empty(x) || + m_util.str.is_string(x)) { + expr_ref len(n, m); + m_rewrite(len); + if (n != len) { + TRACE("seq", tout << "Add length coherence for " << mk_pp(n, m) << "\n";); + add_axiom(mk_eq(n, len, false)); + } + } + else { expr_ref zero(m_autil.mk_int(0), m); expr_ref emp(m_util.str.mk_empty(m.get_sort(x)), m); literal eq1(mk_eq(zero, n, false)); @@ -1223,12 +1254,6 @@ void theory_seq::add_length_axiom(expr* n) { add_axiom(~eq1, eq2); add_axiom(~eq2, eq1); } - if (m_util.str.is_concat(x) || - m_util.str.is_unit(x) || - m_util.str.is_empty(x) || - m_util.str.is_string(x)) { - add_length_coherence_axiom(x); - } } @@ -1638,8 +1663,7 @@ void theory_seq::restart_eh() { } void theory_seq::relevant_eh(app* n) { - if (m_util.str.is_length(n) || - m_util.str.is_index(n) || + if (m_util.str.is_index(n) || m_util.str.is_replace(n) || m_util.str.is_extract(n) || m_util.str.is_at(n) || @@ -1647,6 +1671,11 @@ void theory_seq::relevant_eh(app* n) { is_step(n)) { enque_axiom(n); } + + expr* arg; + if (m_util.str.is_length(n, arg) && !has_length(arg)) { + enforce_length(get_context().get_enode(arg)); + } } @@ -1729,24 +1758,28 @@ expr_ref theory_seq::mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned void theory_seq::add_accept2step(expr* acc) { context& ctx = get_context(); SASSERT(ctx.get_assignment(acc) == l_true); - expr* s, *re; + expr* e, *re; unsigned src; eautomaton* aut = 0; - VERIFY(is_accept(acc, s, re, src, aut)); + VERIFY(is_accept(acc, e, re, src, aut)); if (!aut) return; - if (m_util.str.is_empty(s)) return; + if (m_util.str.is_empty(e)) return; eautomaton::moves mvs; aut->get_moves_from(src, mvs); expr_ref head(m), tail(m), emp(m), step(m); - mk_decompose(s, emp, head, tail); + mk_decompose(e, emp, head, tail); + if (!m_util.is_skolem(e)) { + expr_ref conc(m_util.str.mk_concat(head, tail), m); + add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); + } literal_vector lits; lits.push_back(~ctx.get_literal(acc)); if (aut->is_final_state(src)) { - lits.push_back(mk_eq(emp, s, false)); + lits.push_back(mk_eq(emp, e, false)); } for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move mv = mvs[i]; - step = mk_step(s, tail, re, src, mv.dst(), mv.t()); + step = mk_step(e, tail, re, src, mv.dst(), mv.t()); lits.push_back(mk_literal(step)); } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); @@ -1778,22 +1811,26 @@ void theory_seq::add_step2accept(expr* step) { void theory_seq::add_reject2reject(expr* rej) { context& ctx = get_context(); SASSERT(ctx.get_assignment(rej) == l_true); - expr* s, *re; + expr* e, *re; unsigned src; eautomaton* aut = 0; - VERIFY(is_reject(rej, s, re, src, aut)); + VERIFY(is_reject(rej, e, re, src, aut)); if (!aut) return; - if (m_util.str.is_empty(s)) return; + if (m_util.str.is_empty(e)) return; eautomaton::moves mvs; aut->get_moves_from(src, mvs); expr_ref head(m), tail(m), emp(m), conc(m); - mk_decompose(s, emp, head, tail); + mk_decompose(e, emp, head, tail); + if (!m_util.is_skolem(e)) { + expr_ref conc(m_util.str.mk_concat(head, tail), m); + add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); + } literal rej1 = ctx.get_literal(rej); for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move const& mv = mvs[i]; conc = m_util.str.mk_concat(m_util.str.mk_unit(mv.t()), tail); literal rej2 = mk_reject(tail, re, m_autil.mk_int(mv.dst())); - add_axiom(~rej1, ~mk_eq(s, conc, false), rej2); + add_axiom(~rej1, ~mk_eq(e, conc, false), rej2); } } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index e7f610695..3ecc66c58 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -259,17 +259,17 @@ namespace smt { unsigned m_axioms_head; // index of first axiom to add. unsigned m_branch_variable_head; // index of first equation to examine. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. - obj_hashtable m_length; // is length applied - bool m_model_completion; // during model construction, invent values in canonizer + obj_hashtable m_length; // is length applied model_generator* m_mg; th_rewriter m_rewrite; seq_util m_util; arith_util m_autil; th_trail_stack m_trail_stack; stats m_stats; - symbol m_prefix, m_suffix, m_contains_left, m_contains_right, m_left, m_right, m_accept, m_reject; - symbol m_tail, m_head_elem, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; + symbol m_prefix, m_suffix, m_contains_left, m_contains_right, m_accept, m_reject; + symbol m_tail, m_nth, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; symbol m_extract_prefix, m_at_left, m_at_right; + ptr_vector m_todo; // maintain automata with regular expressions. scoped_ptr_vector m_automata; @@ -348,7 +348,6 @@ namespace smt { void add_replace_axiom(expr* e); void add_extract_axiom(expr* e); void add_length_axiom(expr* n); - void add_length_coherence_axiom(expr* n); bool has_length(expr *e) const { return m_length.contains(e); } void add_length(expr* e); @@ -407,6 +406,9 @@ namespace smt { theory_seq(ast_manager& m); virtual ~theory_seq(); + // model building + app* mk_value(app* a); + }; }; From e2fab0a555b0fee091c83107faf6f57799d69680 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 28 Dec 2015 18:15:48 -0800 Subject: [PATCH 17/35] seq Signed-off-by: Nikolaj Bjorner --- src/math/automata/automaton.h | 30 ++++- src/smt/theory_seq.cpp | 237 ++++++++++++++++++---------------- src/smt/theory_seq.h | 24 ++-- src/util/ref_vector.h | 3 +- 4 files changed, 170 insertions(+), 124 deletions(-) diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index fb6bb31fd..df1d37919 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -296,6 +296,10 @@ public: // src0 - t -> src - e -> dst => src0 - t -> dst // out_degree(dst) = 1, final(dst) => final(src), dst != dst1 // src - e -> dst - t -> dst1 => src - t -> dst1 + + // Generalized: + // Src - E -> dst - t -> dst1 => Src - t -> dst1 if dst is final => each Src is final + // void compress() { for (unsigned i = 0; i < m_delta.size(); ++i) { for (unsigned j = 0; j < m_delta[i].size(); ++j) { @@ -329,7 +333,24 @@ public: } add(move(m, src, dst1, t)); remove(dst, dst1, t); - + } + else if (false && 1 == out_degree(dst) && all_epsilon_in(dst) && init() != dst && !is_final_state(dst)) { + move const& mv = m_delta[dst][0]; + T* t = mv.t(); + unsigned dst1 = mv.dst(); + unsigned_vector src0s; + moves const& mvs = m_delta_inv[dst]; + for (unsigned k = 0; k < mvs.size(); ++k) { + SASSERT(mvs[k].is_epsilon()); + src0s.push_back(mvs[k].src()); + } + for (unsigned k = 0; k < src0s.size(); ++k) { + remove(src0s[k], dst, 0); + add(move(m, src0s[i], dst1, t)); + } + remove(dst, dst1, t); + --j; + continue; } else { continue; @@ -392,9 +413,10 @@ public: return true; } - bool is_deterministic() const { - for (unsigned i = 0; i < m_delta.size(); ++i) { - if (m_delta[i].size() >= 2) return false; + bool all_epsilon_in(unsigned s) { + moves const& mvs = m_delta_inv[s]; + for (unsigned j = 0; j < mvs.size(); ++j) { + if (mvs[j].t()) return false; } return true; } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 983730158..d4ae23203 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -59,6 +59,10 @@ void theory_seq::solution_map::add_trail(map_update op, expr* l, expr* r, enode_ m_deps.push_back(d); } +bool theory_seq::solution_map::is_root(expr* e) const { + return !m_map.contains(e); +} + expr* theory_seq::solution_map::find(expr* e, enode_pair_dependency*& d) { std::pair value; d = 0; @@ -181,31 +185,31 @@ final_check_status theory_seq::final_check_eh() { context & ctx = get_context(); TRACE("seq", display(tout);); if (!check_ineqs()) { - TRACE("seq", tout << "check_ineqs\n";); + TRACE("seq", tout << ">>check_ineqs\n";); return FC_CONTINUE; } if (simplify_and_solve_eqs()) { - TRACE("seq", tout << "solve_eqs\n";); + TRACE("seq", tout << ">>solve_eqs\n";); return FC_CONTINUE; } if (solve_nqs()) { - TRACE("seq", tout << "solve_nqs\n";); + TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; } if (branch_variable()) { - TRACE("seq", tout << "branch_variable\n";); + TRACE("seq", tout << ">>branch_variable\n";); return FC_CONTINUE; } if (!check_length_coherence()) { - TRACE("seq", tout << "check_length_coherence\n";); + TRACE("seq", tout << ">>check_length_coherence\n";); return FC_CONTINUE; } if (propagate_automata()) { - TRACE("seq", tout << "propagate_automata\n";); + TRACE("seq", tout << ">>propagate_automata\n";); return FC_CONTINUE; } if (is_solved()) { - TRACE("seq", tout << "is_solved\n";); + TRACE("seq", tout << ">>is_solved\n";); return FC_DONE; } @@ -307,6 +311,39 @@ bool theory_seq::assume_equality(expr* l, expr* r) { } } +bool theory_seq::propagate_length_coherence(expr* e) { + expr_ref head(m), tail(m), emp(m); + rational lo, hi; + TRACE("seq", tout << "Unsolved " << mk_pp(e, m); + if (!lower_bound(e, lo)) lo = -rational::one(); + if (!upper_bound(e, hi)) hi = -rational::one(); + tout << " lo: " << lo << " hi: " << hi << "\n"; + ); + + if (!lower_bound(e, lo) || !lo.is_pos() || lo >= rational(2048)) { + return false; + } + literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); + expr_ref seq(e, m); + expr_ref_vector elems(m); + unsigned _lo = lo.get_unsigned(); + for (unsigned j = 0; j < _lo; ++j) { + mk_decompose(seq, emp, head, tail); + elems.push_back(head); + seq = tail; + } + elems.push_back(seq); + tail = m_util.str.mk_concat(elems.size(), elems.c_ptr()); + // len(e) >= low => e = tail + add_axiom(~low, mk_eq(e, tail, false)); + assume_equality(tail, e); + if (upper_bound(e, hi)) { + expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); + expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); + add_axiom(~mk_literal(high1), mk_literal(high2)); + } + return true; +} bool theory_seq::check_length_coherence() { if (m_length.empty()) return true; @@ -315,42 +352,13 @@ bool theory_seq::check_length_coherence() { obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); for (; it != end; ++it) { expr* e = *it; - enode_pair_dependency* dep = 0; - expr* f = m_rep.find(e, dep); - if (is_var(f) && f == e) { + if (is_var(e) && m_rep.is_root(e)) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); expr_ref head(m), tail(m); - rational lo, hi; - TRACE("seq", tout << "Unsolved " << mk_pp(e, m); - if (!lower_bound(e, lo)) lo = -rational::one(); - if (!upper_bound(e, hi)) hi = -rational::one(); - tout << " lo: " << lo << " "; - tout << "hi: " << hi << " "; - tout << "\n"; - ctx.display(tout); - ); - - if (lower_bound(e, lo) && lo.is_pos() && lo < rational(512)) { - literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); - expr_ref seq(e, m); - expr_ref_vector elems(m); - unsigned _lo = lo.get_unsigned(); - for (unsigned j = 0; j < _lo; ++j) { - mk_decompose(seq, emp, head, tail); - elems.push_back(head); - seq = tail; - } - elems.push_back(seq); - tail = m_util.str.mk_concat(elems.size(), elems.c_ptr()); - // len(e) >= low => e = tail - add_axiom(~low, mk_eq(e, tail, false)); - assume_equality(tail, e); - if (upper_bound(e, hi)) { - expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); - expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); - add_axiom(~mk_literal(high1), mk_literal(high2)); - } + if (propagate_length_coherence(e)) { + //m_replay_length_coherence.push_back(e); + //m_replay_length_coherence_qhead = m_replay_length_coherence.size(); } else if (!assume_equality(e, emp)) { mk_decompose(e, emp, head, tail); @@ -365,14 +373,18 @@ bool theory_seq::check_length_coherence() { return coherent; } +expr_ref theory_seq::mk_nth(expr* s, expr* idx) { + sort* char_sort = 0; + VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); + return mk_skolem(m_nth, s, idx, 0, char_sort); +} + void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail) { expr* e1, *e2; - sort* char_sort = 0; zstring s; - VERIFY(m_util.is_seq(m.get_sort(e), char_sort)); emp = m_util.str.mk_empty(m.get_sort(e)); if (m_util.str.is_empty(e)) { - head = m_util.str.mk_unit(mk_skolem(m_nth, e, m_autil.mk_int(0), 0, char_sort)); + head = m_util.str.mk_unit(mk_nth(e, m_autil.mk_int(0))); tail = e; } else if (m_util.str.is_string(e, s)) { @@ -393,11 +405,11 @@ void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& expr* s = a->get_arg(0); VERIFY (m_autil.is_numeral(a->get_arg(1), r)); expr* idx = m_autil.mk_int(r.get_unsigned() + 1); - head = m_util.str.mk_unit(mk_skolem(m_nth, s, idx, 0, char_sort)); + head = m_util.str.mk_unit(mk_nth(s, idx)); tail = mk_skolem(m_tail, s, idx); } else { - head = m_util.str.mk_unit(mk_skolem(m_nth, e, m_autil.mk_int(0), 0, char_sort)); + head = m_util.str.mk_unit(mk_nth(e, m_autil.mk_int(0))); tail = mk_skolem(m_tail, e, m_autil.mk_int(0)); } } @@ -750,7 +762,6 @@ bool theory_seq::internalize_term(app* term) { bool_var bv = ctx.mk_bool_var(term); ctx.set_var_theory(bv, get_id()); ctx.mark_as_relevant(bv); - TRACE("seq", tout << mk_pp(term, m) << ": " << bv << "\n";); } else { enode* e = 0; @@ -1064,8 +1075,13 @@ void theory_seq::propagate() { void theory_seq::enque_axiom(expr* e) { TRACE("seq", tout << "add axioms for: " << mk_pp(e, m) << "\n";); - m_trail_stack.push(push_back_vector(m_axioms)); - m_axioms.push_back(e); + if (!m_axiom_set.contains(e)) { + m_axioms.push_back(e); + m_axiom_set.insert(e); + m_trail_stack.push(push_back_vector(m_axioms)); + m_trail_stack.push(insert_obj_trail(m_axiom_set, e));; + + } } void theory_seq::deque_axiom(expr* n) { @@ -1284,17 +1300,19 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { eautomaton* a = get_automaton(e2); if (!a) return; - if (m_util.str.is_empty(e1)) return; + // if (m_util.str.is_empty(e1)) return; + context& ctx = get_context(); - expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); + expr_ref len(m_util.str.mk_length(e1), m); for (unsigned i = 0; i < a->num_states(); ++i) { - literal acc = mk_accept(emp, e2, i); - literal rej = mk_reject(emp, e2, i); + literal acc = mk_accept(e1, len, e2, i); + literal rej = mk_reject(e1, len, e2, i); add_axiom(a->is_final_state(i)?acc:~acc); add_axiom(a->is_final_state(i)?~rej:rej); } + expr_ref zero(m_autil.mk_int(0), m); unsigned_vector states; a->get_epsilon_closure(a->init(), states); literal_vector lits; @@ -1304,11 +1322,11 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { } for (unsigned i = 0; i < states.size(); ++i) { if (is_true) { - lits.push_back(mk_accept(e1, e2, states[i])); + lits.push_back(mk_accept(e1, zero, e2, states[i])); } else { literal nlit = ~lit; - propagate_lit(0, 1, &nlit, mk_reject(e1, e2, states[i])); + propagate_lit(0, 1, &nlit, mk_reject(e1, zero, e2, states[i])); } } if (is_true) { @@ -1316,7 +1334,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { propagate_lit(0, 1, &lit, lits[1]); } else { - TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr());); + TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } } @@ -1474,18 +1492,14 @@ void theory_seq::add_at_axiom(expr* e) { } /** - step(s, tail, re, i, j, t) -> s = t ++ tail + step(s, idx, re, i, j, t) -> nth(s, idx) == t */ void theory_seq::propagate_step(bool_var v, expr* step) { context& ctx = get_context(); - expr* re, *t, *s, *tail, *i, *j; - VERIFY(is_step(step, s, tail, re, i, j, t)); - expr_ref conc(m_util.str.mk_concat(m_util.str.mk_unit(t), tail), m); - expr_ref sr(s, m); - propagate_eq(v, s, conc); - enode* n1 = ensure_enode(step); - enode* n2 = ctx.get_enode(m.mk_true()); - m_eqs.push_back(eq(sr, conc, m_dm.mk_leaf(enode_pair(n1, n2)))); + expr* re, *t, *s, *idx, *i, *j; + VERIFY(is_step(step, s, idx, re, i, j, t)); + expr_ref nth = mk_nth(s, idx); + propagate_eq(v, t, nth); } @@ -1698,19 +1712,24 @@ eautomaton* theory_seq::get_automaton(expr* re) { return result; } -literal theory_seq::mk_accept(expr* s, expr* re, expr* state) { - return mk_literal(mk_skolem(m_accept, s, re, state, m.mk_bool_sort())); +literal theory_seq::mk_accept(expr* s, expr* idx, expr* re, expr* state) { + expr_ref_vector args(m); + args.push_back(s).push_back(idx).push_back(re).push_back(state); + return mk_literal(m_util.mk_skolem(m_accept, args.size(), args.c_ptr(), m.mk_bool_sort())); } -literal theory_seq::mk_reject(expr* s, expr* re, expr* state) { - return mk_literal(mk_skolem(m_reject, s, re, state, m.mk_bool_sort())); +literal theory_seq::mk_reject(expr* s, expr* idx, expr* re, expr* state) { + expr_ref_vector args(m); + args.push_back(s).push_back(idx).push_back(re).push_back(state); + return mk_literal(m_util.mk_skolem(m_reject, args.size(), args.c_ptr(), m.mk_bool_sort())); } -bool theory_seq::is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { +bool theory_seq::is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut) { if (is_skolem(ar, e)) { rational r; s = to_app(e)->get_arg(0); - re = to_app(e)->get_arg(1); - VERIFY(m_autil.is_numeral(to_app(e)->get_arg(2), r)); + idx = to_app(e)->get_arg(1); + re = to_app(e)->get_arg(2); + VERIFY(m_autil.is_numeral(to_app(e)->get_arg(3), r)); SASSERT(r.is_unsigned()); i = r.get_unsigned(); aut = m_re2aut[re]; @@ -1725,10 +1744,10 @@ bool theory_seq::is_step(expr* e) const { return is_skolem(m_aut_step, e); } -bool theory_seq::is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const { +bool theory_seq::is_step(expr* e, expr*& s, expr*& idx, expr*& re, expr*& i, expr*& j, expr*& t) const { if (is_step(e)) { s = to_app(e)->get_arg(0); - tail = to_app(e)->get_arg(1); + idx = to_app(e)->get_arg(1); re = to_app(e)->get_arg(2); i = to_app(e)->get_arg(3); j = to_app(e)->get_arg(4); @@ -1740,11 +1759,9 @@ bool theory_seq::is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, ex } } -expr_ref theory_seq::mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t) { +expr_ref theory_seq::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned j, expr* t) { expr_ref_vector args(m); - args.push_back(s); - args.push_back(tail); - args.push_back(re); + args.push_back(s).push_back(idx).push_back(re); args.push_back(m_autil.mk_int(i)); args.push_back(m_autil.mk_int(j)); args.push_back(t); @@ -1753,33 +1770,32 @@ expr_ref theory_seq::mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned /** - acc & s != emp -> \/ step_i_t_j + acc(s, idx, re, i) -> \/ step(s, idx, re, i, j, t) if i is non-final + acc(s, idx, re, i) -> len(s) <= idx \/ step(s, idx, re, i, j, t) if i is final + acc(s, idx, re, i) -> len(s) >= idx */ void theory_seq::add_accept2step(expr* acc) { context& ctx = get_context(); SASSERT(ctx.get_assignment(acc) == l_true); - expr* e, *re; + expr *e, * idx, *re; + expr_ref step(m); unsigned src; eautomaton* aut = 0; - VERIFY(is_accept(acc, e, re, src, aut)); - if (!aut) return; - if (m_util.str.is_empty(e)) return; + VERIFY(is_accept(acc, e, idx, re, src, aut)); + if (!aut || m_util.str.is_length(idx)) return; + SASSERT(m_autil.is_numeral(idx)); eautomaton::moves mvs; aut->get_moves_from(src, mvs); - expr_ref head(m), tail(m), emp(m), step(m); - mk_decompose(e, emp, head, tail); - if (!m_util.is_skolem(e)) { - expr_ref conc(m_util.str.mk_concat(head, tail), m); - add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); - } + + expr_ref len(m_util.str.mk_length(e), m); literal_vector lits; lits.push_back(~ctx.get_literal(acc)); if (aut->is_final_state(src)) { - lits.push_back(mk_eq(emp, e, false)); + lits.push_back(mk_literal(m_autil.mk_le(len, idx))); } for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move mv = mvs[i]; - step = mk_step(e, tail, re, src, mv.dst(), mv.t()); + step = mk_step(e, idx, re, src, mv.dst(), mv.t()); lits.push_back(mk_literal(step)); } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); @@ -1787,50 +1803,53 @@ void theory_seq::add_accept2step(expr* acc) { ctx.mark_as_relevant(lits[i]); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + add_axiom(~ctx.get_literal(acc), mk_literal(m_autil.mk_ge(len, idx))); } /** - acc(s, re, i) & step(head, tail, re, i, j, t) => acc(tail, re, j) + acc(s, idx, re, i) & step(s, idx, re, i, j, t) => acc(s, idx + 1, re, j) */ void theory_seq::add_step2accept(expr* step) { context& ctx = get_context(); SASSERT(ctx.get_assignment(step) == l_true); - expr* re, *t, *s, *tail, *i, *j; - VERIFY(is_step(step, s, tail, re, i, j, t)); - literal acc1 = mk_accept(s, re, i); - literal acc2 = mk_accept(tail, re, j); + rational r; + expr* re, *t, *s, *idx, *i, *j; + VERIFY(is_step(step, s, idx, re, i, j, t)); + VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); + expr_ref idx1(m_autil.mk_int(r.get_unsigned() + 1), m); + literal acc1 = mk_accept(s, idx, re, i); + literal acc2 = mk_accept(s, idx1, re, j); add_axiom(~acc1, ~ctx.get_literal(step), acc2); } /* - rej(s, re, i) & s = t ++ tail => rej(tail, re, j) + rej(s, idx, re, i) & nth(s,idx) = t & idx < len(s) => rej(s, idx + 1 re, j) + rej(s, idx, re, i) => idx <= len(s) */ void theory_seq::add_reject2reject(expr* rej) { context& ctx = get_context(); SASSERT(ctx.get_assignment(rej) == l_true); - expr* e, *re; + expr* e, *idx, *re; unsigned src; + rational r; eautomaton* aut = 0; - VERIFY(is_reject(rej, e, re, src, aut)); - if (!aut) return; - if (m_util.str.is_empty(e)) return; + VERIFY(is_reject(rej, e, idx, re, src, aut)); + if (!aut || m_util.str.is_length(idx)) return; + VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); + expr_ref idx1(m_autil.mk_int(r.get_unsigned() + 1), m); eautomaton::moves mvs; aut->get_moves_from(src, mvs); - expr_ref head(m), tail(m), emp(m), conc(m); - mk_decompose(e, emp, head, tail); - if (!m_util.is_skolem(e)) { - expr_ref conc(m_util.str.mk_concat(head, tail), m); - add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); - } literal rej1 = ctx.get_literal(rej); + expr_ref len(m_util.str.mk_length(e), m); + add_axiom(~rej1, mk_literal(m_autil.mk_ge(len, idx))); for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move const& mv = mvs[i]; - conc = m_util.str.mk_concat(m_util.str.mk_unit(mv.t()), tail); - literal rej2 = mk_reject(tail, re, m_autil.mk_int(mv.dst())); - add_axiom(~rej1, ~mk_eq(e, conc, false), rej2); + expr_ref nth = mk_nth(e, idx); + literal rej2 = mk_reject(e, idx1, re, m_autil.mk_int(mv.dst())); + add_axiom(~rej1, ~mk_eq(nth, mv.t(), false), ~mk_literal(m_autil.mk_ge(len, idx)), rej2); } } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 3ecc66c58..024a5b8da 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -72,6 +72,7 @@ namespace smt { void add_cache(expr* v, expr_dep& r) { m_cache.insert(v, r); } bool find_cache(expr* v, expr_dep& r) { return m_cache.find(v, r); } expr* find(expr* e, enode_pair_dependency*& d); + bool is_root(expr* e) const; void cache(expr* e, expr* r, enode_pair_dependency* d); void reset_cache() { m_cache.reset(); } void push_scope() { m_limit.push_back(m_updates.size()); } @@ -256,6 +257,7 @@ namespace smt { expr_ref_vector m_ineqs; // inequalities to check solution against exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. + obj_hashtable m_axiom_set; unsigned m_axioms_head; // index of first axiom to add. unsigned m_branch_variable_head; // index of first equation to examine. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. @@ -305,7 +307,8 @@ namespace smt { bool branch_variable(); // branch on a variable bool split_variable(); // split a variable bool is_solved(); - bool check_length_coherence(); + bool check_length_coherence(); + bool propagate_length_coherence(expr* e); bool check_ineq_coherence(); bool pre_process_eqs(bool simplify_or_solve, bool& propagated); @@ -368,6 +371,7 @@ namespace smt { void mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); + expr_ref mk_nth(expr* s, expr* idx); bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); @@ -375,19 +379,19 @@ namespace smt { // automata utilities void propagate_in_re(expr* n, bool is_true); eautomaton* get_automaton(expr* e); - literal mk_accept(expr* s, expr* re, expr* state); - literal mk_accept(expr* s, expr* re, unsigned i) { return mk_accept(s, re, m_autil.mk_int(i)); } + literal mk_accept(expr* s, expr* idx, expr* re, expr* state); + literal mk_accept(expr* s, expr* idx, expr* re, unsigned i) { return mk_accept(s, idx, re, m_autil.mk_int(i)); } bool is_accept(expr* acc) const { return is_skolem(m_accept, acc); } - bool is_accept(expr* acc, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { - return is_acc_rej(m_accept, acc, s, re, i, aut); + bool is_accept(expr* acc, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut) { + return is_acc_rej(m_accept, acc, s, idx, re, i, aut); } - literal mk_reject(expr* s, expr* re, expr* state); - literal mk_reject(expr* s, expr* re, unsigned i) { return mk_reject(s, re, m_autil.mk_int(i)); } + literal mk_reject(expr* s, expr* idx, expr* re, expr* state); + literal mk_reject(expr* s, expr* idx, expr* re, unsigned i) { return mk_reject(s, idx, re, m_autil.mk_int(i)); } bool is_reject(expr* rej) const { return is_skolem(m_reject, rej); } - bool is_reject(expr* rej, expr*& s, expr*& re, unsigned& i, eautomaton*& aut) { - return is_acc_rej(m_reject, rej, s, re, i, aut); + bool is_reject(expr* rej, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut) { + return is_acc_rej(m_reject, rej, s, idx, re, i, aut); } - bool is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& re, unsigned& i, eautomaton*& aut); + bool is_acc_rej(symbol const& ar, expr* e, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut); expr_ref mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t); bool is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const; bool is_step(expr* e) const; diff --git a/src/util/ref_vector.h b/src/util/ref_vector.h index b651a555d..fdfb94d93 100644 --- a/src/util/ref_vector.h +++ b/src/util/ref_vector.h @@ -89,9 +89,10 @@ public: m_nodes.shrink(sz); } - void push_back(T * n) { + ref_vector_core& push_back(T * n) { inc_ref(n); m_nodes.push_back(n); + return *this; } void pop_back() { From bd9b5b57353dd518f7353b76deeedd5294c1d9bb Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 29 Dec 2015 10:13:19 -0800 Subject: [PATCH 18/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 4 + src/math/automata/automaton.h | 26 ++---- src/smt/smt_model_generator.cpp | 1 + src/smt/theory_seq.cpp | 139 +++++++++++++++++++++++------- src/smt/theory_seq.h | 17 ++-- 5 files changed, 133 insertions(+), 54 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index aa27489ad..a5c3fb403 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -39,7 +39,10 @@ re2automaton::re2automaton(ast_manager& m): m(m), u(m) {} eautomaton* re2automaton::operator()(expr* e) { eautomaton* r = re2aut(e); if (r) { + //display_expr1 disp(m); + //r->display(std::cout, disp); r->compress(); + //r->display(std::cout, disp); } return r; } @@ -61,6 +64,7 @@ eautomaton* re2automaton::re2aut(expr* e) { else if (u.re.is_star(e, e1) && (a = re2aut(e1))) { a->add_final_to_init_moves(); a->add_init_to_final_states(); + return a.detach(); } else if (u.re.is_plus(e, e1) && (a = re2aut(e1))) { diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index df1d37919..54cd02832 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -209,24 +209,16 @@ public: moves mvs; unsigned_vector final; unsigned init = 0; - if (a.has_single_final_sink() && b.initial_state_is_source() && b.init() == 0) { - unsigned offset2 = a.num_states(); - init = a.init(); - append_moves(0, a, mvs); - append_moves(offset2, b, mvs); - append_final(offset2, b, final); - } - else { - unsigned offset1 = 1; - unsigned offset2 = a.num_states() + offset1; - mvs.push_back(move(m, 0, a.init() + offset1)); - append_moves(offset1, a, mvs); - for (unsigned i = 0; i < a.m_final_states.size(); ++i) { - mvs.push_back(move(m, a.m_final_states[i], b.init() + offset2)); - } - append_moves(offset2, b, mvs); - append_final(offset2, b, final); + unsigned offset1 = 1; + unsigned offset2 = a.num_states() + offset1; + mvs.push_back(move(m, 0, a.init() + offset1)); + append_moves(offset1, a, mvs); + for (unsigned i = 0; i < a.m_final_states.size(); ++i) { + mvs.push_back(move(m, a.m_final_states[i] + offset1, b.init() + offset2)); } + append_moves(offset2, b, mvs); + append_final(offset2, b, final); + return alloc(automaton, m, init, final, mvs); } diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 16cf18d2e..88e9761b4 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -361,6 +361,7 @@ namespace smt { } else { enode * child = d.get_enode(); + TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_owner(), m_manager) << "): " << mk_pp(child->get_owner(), m_manager) << " " << mk_pp(child->get_root()->get_owner(), m_manager) << "\n";); child = child->get_root(); app * val = 0; m_root2value.find(child, val); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index d4ae23203..63bdedd1e 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -77,6 +77,14 @@ expr* theory_seq::solution_map::find(expr* e, enode_pair_dependency*& d) { return result; } +expr* theory_seq::solution_map::find(expr* e) { + std::pair value; + while (m_map.find(e, value)) { + e = value.first; + } + return e; +} + void theory_seq::solution_map::pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; m_cache.reset(); @@ -185,26 +193,32 @@ final_check_status theory_seq::final_check_eh() { context & ctx = get_context(); TRACE("seq", display(tout);); if (!check_ineqs()) { + ++m_stats.m_check_ineqs; TRACE("seq", tout << ">>check_ineqs\n";); return FC_CONTINUE; } if (simplify_and_solve_eqs()) { + ++m_stats.m_solve_eqs; TRACE("seq", tout << ">>solve_eqs\n";); return FC_CONTINUE; } if (solve_nqs()) { + ++m_stats.m_solve_nqs; TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; } if (branch_variable()) { + ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); return FC_CONTINUE; } if (!check_length_coherence()) { + ++m_stats.m_check_length_coherence; TRACE("seq", tout << ">>check_length_coherence\n";); return FC_CONTINUE; } if (propagate_automata()) { + ++m_stats.m_propagate_automata; TRACE("seq", tout << ">>propagate_automata\n";); return FC_CONTINUE; } @@ -312,23 +326,27 @@ bool theory_seq::assume_equality(expr* l, expr* r) { } bool theory_seq::propagate_length_coherence(expr* e) { - expr_ref head(m), tail(m), emp(m); + expr_ref head(m), tail(m); rational lo, hi; + + if (!is_var(e) || !m_rep.is_root(e)) { + return false; + } + if (!lower_bound(e, lo) || !lo.is_pos() || lo >= rational(2048)) { + return false; + } TRACE("seq", tout << "Unsolved " << mk_pp(e, m); if (!lower_bound(e, lo)) lo = -rational::one(); if (!upper_bound(e, hi)) hi = -rational::one(); tout << " lo: " << lo << " hi: " << hi << "\n"; ); - if (!lower_bound(e, lo) || !lo.is_pos() || lo >= rational(2048)) { - return false; - } literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); expr_ref seq(e, m); expr_ref_vector elems(m); unsigned _lo = lo.get_unsigned(); for (unsigned j = 0; j < _lo; ++j) { - mk_decompose(seq, emp, head, tail); + mk_decompose(seq, head, tail); elems.push_back(head); seq = tail; } @@ -342,6 +360,10 @@ bool theory_seq::propagate_length_coherence(expr* e) { expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); } + + //m_replay_length_coherence.push_back(e); + //m_replay_length_coherence_qhead = m_replay_length_coherence.size(); + return true; } @@ -357,14 +379,12 @@ bool theory_seq::check_length_coherence() { expr_ref head(m), tail(m); if (propagate_length_coherence(e)) { - //m_replay_length_coherence.push_back(e); - //m_replay_length_coherence_qhead = m_replay_length_coherence.size(); } else if (!assume_equality(e, emp)) { - mk_decompose(e, emp, head, tail); + mk_decompose(e, head, tail); // e = emp \/ e = unit(head.elem(e))*tail(e) expr_ref conc(m_util.str.mk_concat(head, tail), m); - add_axiom(mk_eq(e, emp, false), mk_eq(e, conc, false)); + add_axiom(mk_eq_empty(e), mk_eq(e, conc, false)); assume_equality(tail, emp); } return false; @@ -379,10 +399,9 @@ expr_ref theory_seq::mk_nth(expr* s, expr* idx) { return mk_skolem(m_nth, s, idx, 0, char_sort); } -void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail) { +void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { expr* e1, *e2; zstring s; - emp = m_util.str.mk_empty(m.get_sort(e)); if (m_util.str.is_empty(e)) { head = m_util.str.mk_unit(mk_nth(e, m_autil.mk_int(0))); tail = e; @@ -393,7 +412,7 @@ void theory_seq::mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& } else if (m_util.str.is_unit(e)) { head = e; - tail = emp; + tail = m_util.str.mk_empty(m.get_sort(e)); } else if (m_util.str.is_concat(e, e1, e2) && m_util.str.is_unit(e1)) { head = e1; @@ -585,7 +604,7 @@ bool theory_seq::is_var(expr* a) { } -bool theory_seq::is_head_elem(expr* e) const { +bool theory_seq::is_nth(expr* e) const { return is_skolem(m_nth, e); } @@ -884,6 +903,13 @@ void theory_seq::display_deps(std::ostream& out, enode_pair_dependency* dep) con void theory_seq::collect_statistics(::statistics & st) const { st.update("seq num splits", m_stats.m_num_splits); st.update("seq num reductions", m_stats.m_num_reductions); + st.update("e", m_stats.m_propagate_automata); + st.update("seq length coherence", m_stats.m_check_length_coherence); + st.update("seq branch", m_stats.m_branch_variable); + st.update("seq solve !=", m_stats.m_solve_nqs); + st.update("seq solve =", m_stats.m_solve_eqs); + st.update("seq check negations", m_stats.m_check_ineqs); + } void theory_seq::init_model(model_generator & mg) { @@ -910,7 +936,6 @@ public: if (values.empty()) { return th.mk_value(n); } - SASSERT(values.size() == n->get_num_args()); return th.mk_value(mg.get_manager().mk_app(n->get_decl(), values.size(), values.c_ptr())); } }; @@ -918,13 +943,35 @@ public: model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { context& ctx = get_context(); - enode_pair_dependency* dep = 0; - expr* e = m_rep.find(n->get_owner(), dep); - expr* e1, *e2; + expr_ref e(m); + expr* e1; + ptr_vector concats; + get_concat(n->get_owner(), concats); + switch (concats.size()) { + case 0: + e = m_util.str.mk_empty(m.get_sort(n->get_owner())); + break; + case 1: + e = concats[0]; + SASSERT(!m_util.str.is_concat(e)); + break; + default: + e = m_rep.find(n->get_owner()); + SASSERT(m_util.str.is_concat(e)); + break; + } seq_value_proc* sv = alloc(seq_value_proc, *this, to_app(e)); - if (m_util.str.is_concat(e, e1, e2)) { - sv->add_dependency(ctx.get_enode(e1)); - sv->add_dependency(ctx.get_enode(e2)); + TRACE("seq", tout << mk_pp(n->get_owner(), m) << " "; + for (unsigned i = 0; i < concats.size(); ++i) { + tout << mk_pp(concats[i], m) << " "; + } + tout << "\n"; + ); + + if (concats.size() > 1) { + for (unsigned i = 0; i < concats.size(); ++i) { + sv->add_dependency(ctx.get_enode(concats[i])); + } } else if (m_util.str.is_unit(e, e1)) { sv->add_dependency(ctx.get_enode(e1)); @@ -932,6 +979,22 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { return sv; } +void theory_seq::get_concat(expr* e, ptr_vector& concats) { + expr* e1, *e2; + while (true) { + e = m_rep.find(e); + if (m_util.str.is_concat(e, e1, e2)) { + get_concat(e1, concats); + e = e2; + continue; + } + if (!m_util.str.is_empty(e)) { + concats.push_back(e); + } + return; + } +} + app* theory_seq::mk_value(app* e) { expr* e1; expr_ref result(e, m); @@ -964,7 +1027,7 @@ app* theory_seq::mk_value(app* e) { result = e; } } - else if (is_head_elem(e)) { + else if (is_nth(e)) { enode* n = get_context().get_enode(e)->get_root(); enode* n0 = n; bool found_value = false; @@ -1088,6 +1151,9 @@ void theory_seq::deque_axiom(expr* n) { if (m_util.str.is_length(n)) { add_length_axiom(n); } + else if (m_util.str.is_empty(n) && !has_length(n)) { + enforce_length(get_context().get_enode(n)); + } else if (m_util.str.is_index(n)) { add_indexof_axiom(n); } @@ -1120,8 +1186,7 @@ void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) { expr_ref s1c(m_util.str.mk_concat(s1, c), m); expr_ref lc(m_util.str.mk_length(c), m); expr_ref one(m_autil.mk_int(1), m); - expr_ref emp(m_util.str.mk_empty(m.get_sort(s)), m); - literal s_eq_emp = mk_eq(s, emp, false); + literal s_eq_emp = mk_eq_empty(s); add_axiom(lit1, lit2, s_eq_emp, mk_eq(s, s1c, false)); add_axiom(lit1, lit2, s_eq_emp, mk_eq(lc, one, false)); add_axiom(lit1, lit2, s_eq_emp, ~mk_literal(m_util.str.mk_contains(s, m_util.str.mk_concat(x, s1)))); @@ -1160,7 +1225,6 @@ void theory_seq::add_indexof_axiom(expr* i) { expr_ref emp(m), minus_one(m), zero(m), xsy(m); minus_one = m_autil.mk_int(-1); zero = m_autil.mk_int(0); - emp = m_util.str.mk_empty(m.get_sort(s)); literal offset_ne_zero = null_literal; bool is_num = m_autil.is_numeral(offset, r); if (is_num && r.is_zero()) { @@ -1174,7 +1238,7 @@ void theory_seq::add_indexof_axiom(expr* i) { expr_ref y = mk_skolem(m_contains_right, t, s); xsy = m_util.str.mk_concat(x,s,y); literal cnt = mk_literal(m_util.str.mk_contains(t, s)); - literal eq_empty = mk_eq(s, emp, false); + literal eq_empty = mk_eq_empty(s); add_axiom(offset_ne_zero, cnt, mk_eq(i, minus_one, false)); add_axiom(offset_ne_zero, ~eq_empty, mk_eq(i, zero, false)); add_axiom(offset_ne_zero, ~cnt, eq_empty, mk_eq(t, xsy, false)); @@ -1263,12 +1327,12 @@ void theory_seq::add_length_axiom(expr* n) { } else { expr_ref zero(m_autil.mk_int(0), m); - expr_ref emp(m_util.str.mk_empty(m.get_sort(x)), m); - literal eq1(mk_eq(zero, n, false)); - literal eq2(mk_eq(x, emp, false)); add_axiom(mk_literal(m_autil.mk_ge(n, zero))); - add_axiom(~eq1, eq2); - add_axiom(~eq2, eq1); + + //expr_ref emp(m_util.str.mk_empty(m.get_sort(x)), m); + //literal eq1(mk_eq(zero, n, false)); + //literal eq2(mk_eq(x, emp, false)); + //add_axiom(~eq1, eq2); } } @@ -1510,6 +1574,17 @@ literal theory_seq::mk_literal(expr* _e) { return ctx.get_literal(e); } +literal theory_seq::mk_eq_empty(expr* _e) { + expr_ref e(_e, m); + context& ctx = get_context(); + SASSERT(m_util.is_seq(e)); + expr_ref emp(m); + emp = m_util.str.mk_empty(m.get_sort(e)); + literal lit = mk_eq(e, emp, false); + ctx.force_phase(lit); + return lit; +} + void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4) { context& ctx = get_context(); literal_vector lits; @@ -1681,8 +1756,8 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_replace(n) || m_util.str.is_extract(n) || m_util.str.is_at(n) || - m_util.str.is_string(n) || - is_step(n)) { + m_util.str.is_empty(n) || + m_util.str.is_string(n)) { enque_axiom(n); } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 024a5b8da..5760fb437 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -72,6 +72,7 @@ namespace smt { void add_cache(expr* v, expr_dep& r) { m_cache.insert(v, r); } bool find_cache(expr* v, expr_dep& r) { return m_cache.find(v, r); } expr* find(expr* e, enode_pair_dependency*& d); + expr* find(expr* e); bool is_root(expr* e) const; void cache(expr* e, expr* r, enode_pair_dependency* d); void reset_cache() { m_cache.reset(); } @@ -246,6 +247,12 @@ namespace smt { void reset() { memset(this, 0, sizeof(stats)); } unsigned m_num_splits; unsigned m_num_reductions; + unsigned m_propagate_automata; + unsigned m_check_length_coherence; + unsigned m_branch_variable; + unsigned m_solve_nqs; + unsigned m_solve_eqs; + unsigned m_check_ineqs; }; ast_manager& m; enode_pair_dependency_manager m_dm; @@ -335,13 +342,13 @@ namespace smt { bool occurs(expr* a, expr* b); bool is_var(expr* b); bool add_solution(expr* l, expr* r, enode_pair_dependency* dep); - bool is_left_select(expr* a, expr*& b); - bool is_right_select(expr* a, expr*& b); - bool is_head_elem(expr* a) const; + bool is_nth(expr* a) const; + expr_ref mk_nth(expr* s, expr* idx); expr_ref canonize(expr* e, enode_pair_dependency*& eqs); expr_ref expand(expr* e, enode_pair_dependency*& eqs); void add_dependency(enode_pair_dependency*& dep, enode* a, enode* b); + void get_concat(expr* e, ptr_vector& concats); // terms whose meaning are encoded using axioms. void enque_axiom(expr* e); @@ -360,6 +367,7 @@ namespace smt { void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); literal mk_literal(expr* n); + literal mk_eq_empty(expr* n); void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal); expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); @@ -369,9 +377,8 @@ namespace smt { bool upper_bound(expr* s, rational& hi); bool get_length(expr* s, rational& val); - void mk_decompose(expr* e, expr_ref& emp, expr_ref& head, expr_ref& tail); + void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); - expr_ref mk_nth(expr* s, expr* idx); bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); From 746d26e744cadb5e0b64f66b9637b15d00ab6eab Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 29 Dec 2015 21:14:52 -0800 Subject: [PATCH 19/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 24 +- src/ast/seq_decl_plugin.cpp | 5 +- src/smt/theory_seq.cpp | 540 +++++++++++++++++++----------- src/smt/theory_seq.h | 20 +- 4 files changed, 385 insertions(+), 204 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index a5c3fb403..77baa36fc 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -328,9 +328,16 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { expr_ref_vector as(m()), bs(m()); m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); + bool all_values = true; + + for (unsigned i = 0; all_values && i < bs.size(); ++i) { + all_values = m().is_value(bs[i].get()); + } + bool found = false; for (unsigned i = 0; !found && i < as.size(); ++i) { if (bs.size() > as.size() - i) break; + all_values &= m().is_value(as[i].get()); unsigned j = 0; for (; j < bs.size() && as[j+i].get() == bs[j].get(); ++j) {}; found = j == bs.size(); @@ -339,6 +346,10 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { result = m().mk_true(); return BR_DONE; } + if (all_values) { + result = m().mk_false(); + return BR_DONE; + } return BR_FAILED; } @@ -460,11 +471,22 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); unsigned i = 0; - for (; i < as.size() && i < bs.size() && as[i].get() == bs[i].get(); ++i) {}; + bool all_values = true; + for (; i < as.size() && i < bs.size(); ++i) { + all_values &= m().is_value(as[i].get()) && m().is_value(bs[i].get()); + if (as[i].get() != bs[i].get()) { + break; + } + }; if (i == as.size()) { result = m().mk_true(); return BR_DONE; } + SASSERT(i < as.size()); + if (all_values && (i < bs.size() || m_util.str.is_unit(as[i+1].get()))) { + result = m().mk_false(); + return BR_DONE; + } if (i == bs.size()) { expr_ref_vector es(m()); for (unsigned j = i; j < as.size(); ++j) { diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index e516d4ea5..57ccd1a3e 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -610,7 +610,10 @@ app* seq_decl_plugin::mk_string(zstring const& s) { bool seq_decl_plugin::is_value(app* e) const { - return is_app_of(e, m_family_id, OP_STRING_CONST); + return + is_app_of(e, m_family_id, OP_STRING_CONST) || + (is_app_of(e, m_family_id, OP_SEQ_UNIT) && + m_manager->is_value(e->get_arg(0))); } app* seq_util::mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range) { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 63bdedd1e..b9886a609 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -15,6 +15,8 @@ Author: Revision History: + // Use instead reference counts for dependencies to GC? + --*/ #include "value_factory.h" @@ -153,7 +155,6 @@ theory_seq::theory_seq(ast_manager& m): m(m), m_rep(m, m_dm), m_factory(0), - m_ineqs(m), m_exclude(m), m_axioms(m), m_axioms_head(0), @@ -163,9 +164,7 @@ theory_seq::theory_seq(ast_manager& m): m_util(m), m_autil(m), m_trail_stack(*this), - m_accepts_qhead(0), - m_rejects_qhead(0), - m_steps_qhead(0) { + m_atoms_qhead(0) { m_prefix = "seq.prefix.suffix"; m_suffix = "seq.suffix.prefix"; m_contains_left = "seq.contains.left"; @@ -192,11 +191,6 @@ theory_seq::~theory_seq() { final_check_status theory_seq::final_check_eh() { context & ctx = get_context(); TRACE("seq", display(tout);); - if (!check_ineqs()) { - ++m_stats.m_check_ineqs; - TRACE("seq", tout << ">>check_ineqs\n";); - return FC_CONTINUE; - } if (simplify_and_solve_eqs()) { ++m_stats.m_solve_eqs; TRACE("seq", tout << ">>solve_eqs\n";); @@ -230,24 +224,6 @@ final_check_status theory_seq::final_check_eh() { return FC_GIVEUP; } -bool theory_seq::check_ineqs() { - context & ctx = get_context(); - for (unsigned i = 0; i < m_ineqs.size(); ++i) { - expr* a = m_ineqs[i].get(); - enode_pair_dependency* eqs = 0; - expr_ref b = canonize(a, eqs); - if (m.is_true(b)) { - TRACE("seq", tout << "Evaluates to false: " << mk_pp(a,m) << "\n";); - ctx.internalize(a, false); - propagate_lit(eqs, ctx.get_literal(a)); - return false; - } - else if (!m.is_false(b)) { - TRACE("seq", tout << "Disequality is undetermined: " << mk_pp(a, m) << " " << b << "\n";); - } - } - return true; -} bool theory_seq::branch_variable() { context& ctx = get_context(); @@ -284,7 +260,7 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { expr_ref v0(m), v(m); v0 = m_util.str.mk_empty(m.get_sort(l)); - if (assume_equality(l, v0)) { + if (l_false != assume_equality(l, v0)) { return true; } for (unsigned j = 0; j < rs.size(); ++j) { @@ -296,33 +272,44 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { for (unsigned k = 1; k < s.length(); ++k) { v = m_util.str.mk_string(s.extract(0, k)); if (v0) v = m_util.str.mk_concat(v0, v); - if (assume_equality(l, v)) { + if (l_false != assume_equality(l, v)) { return true; } } } v0 = (j == 0)? rs[0] : m_util.str.mk_concat(v0, rs[j]); - if (assume_equality(l, v0)) { + if (l_false != assume_equality(l, v0)) { return true; } } return false; } -bool theory_seq::assume_equality(expr* l, expr* r) { +lbool theory_seq::assume_equality(expr* l, expr* r) { context& ctx = get_context(); if (m_exclude.contains(l, r)) { - return false; + return l_false; } - else { - TRACE("seq", tout << mk_pp(l, m) << " = " << mk_pp(r, m) << "\n";); - enode* n1 = ensure_enode(l); - enode* n2 = ensure_enode(r); - ctx.mark_as_relevant(n1); - ctx.mark_as_relevant(n2); - ctx.assume_eq(n1, n2); - return true; + + expr_ref eq(m.mk_eq(l, r), m); + m_rewrite(eq); + if (m.is_true(eq)) { + return l_true; } + if (m.is_false(eq)) { + return l_false; + } + + TRACE("seq", tout << mk_pp(l, m) << " = " << mk_pp(r, m) << "\n";); + enode* n1 = ensure_enode(l); + enode* n2 = ensure_enode(r); + if (n1->get_root() == n2->get_root()) { + return l_true; + } + ctx.mark_as_relevant(n1); + ctx.mark_as_relevant(n2); + ctx.assume_eq(n1, n2); + return l_undef; } bool theory_seq::propagate_length_coherence(expr* e) { @@ -377,10 +364,8 @@ bool theory_seq::check_length_coherence() { if (is_var(e) && m_rep.is_root(e)) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); expr_ref head(m), tail(m); - - if (propagate_length_coherence(e)) { - } - else if (!assume_equality(e, emp)) { + if (!propagate_length_coherence(e) && + l_false == assume_equality(e, emp)) { mk_decompose(e, head, tail); // e = emp \/ e = unit(head.elem(e))*tail(e) expr_ref conc(m_util.str.mk_concat(head, tail), m); @@ -433,19 +418,6 @@ void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { } } -bool theory_seq::check_ineq_coherence() { - bool all_false = true; - for (unsigned i = 0; all_false && i < m_ineqs.size(); ++i) { - expr* a = m_ineqs[i].get(); - enode_pair_dependency* eqs = 0; - expr_ref b = canonize(a, eqs); - all_false = m.is_false(b); - if (all_false) { - TRACE("seq", tout << "equality is undetermined: " << mk_pp(a, m) << " " << b << "\n";); - } - } - return all_false; -} /* - Eqs = 0 @@ -457,15 +429,10 @@ bool theory_seq::is_solved() { if (!m_eqs.empty()) { return false; } - if (!check_ineq_coherence()) { - return false; - } for (unsigned i = 0; i < m_automata.size(); ++i) { if (!m_automata[i]) return false; - } - + } return true; - } void theory_seq::propagate_lit(enode_pair_dependency* dep, unsigned n, literal const* lits, literal lit) { @@ -565,10 +532,6 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, enode_pair_dependency* deps, bo propagated = add_solution(rh, lh, deps) || propagated; return true; } - // Use instead reference counts for dependencies to GC? - - // TBD: Solutions to units are not necessarily variables, but - // they may induce new equations. return false; } @@ -615,7 +578,6 @@ bool theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { context& ctx = get_context(); TRACE("seq", tout << mk_pp(l, m) << " ==> " << mk_pp(r, m) << "\n";); m_rep.update(l, r, deps); - // TBD: skip new equalities for non-internalized terms. if (ctx.e_internalized(l) && ctx.e_internalized(r) && ctx.get_enode(l)->get_root() != ctx.get_enode(r)->get_root()) { propagate_eq(deps, ctx.get_enode(l), ctx.get_enode(r)); return true; @@ -825,7 +787,6 @@ void theory_seq::apply_sort_cnstr(enode* n, sort* s) { void theory_seq::display(std::ostream & out) const { if (m_eqs.size() == 0 && m_nqs.size() == 0 && - m_ineqs.empty() && m_rep.empty() && m_exclude.empty()) { return; @@ -839,12 +800,6 @@ void theory_seq::display(std::ostream & out) const { out << "Disequations:\n"; display_disequations(out); } - if (!m_ineqs.empty()) { - out << "Negative constraints:\n"; - for (unsigned i = 0; i < m_ineqs.size(); ++i) { - out << mk_pp(m_ineqs[i], m) << "\n"; - } - } if (!m_re2aut.empty()) { out << "Regex\n"; obj_map::iterator it = m_re2aut.begin(), end = m_re2aut.end(); @@ -908,7 +863,6 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq branch", m_stats.m_branch_variable); st.update("seq solve !=", m_stats.m_solve_nqs); st.update("seq solve =", m_stats.m_solve_eqs); - st.update("seq check negations", m_stats.m_check_ineqs); } @@ -1195,75 +1149,75 @@ void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) { /* // index of s in t starting at offset. - let i = Index(t, s, 0): + let i = Index(t, s, offset): - len(t) = 0 => i = -1 + offset >= len(t) => i = -1 + + offset fixed to 0: + len(t) != 0 & !contains(t, s) => i = -1 len(t) != 0 & contains(t, s) => t = xsy & i = len(x) len(t) != 0 & contains(t, s) & s != emp => tightest_prefix(x, s) - let i = Index(t, s, offset) + offset not fixed: - - 0 <= offset < len(t) => xy = t & len(x) = offset & (-1 = indexof(t, s, 0) => -1 = i) - & (indexof(t, s, 0) >= 0 => indexof(t, s, 0) + offset = i) - - - offset = len(t) => i = -1 - - if offset < 0 or offset >= len(t) + 0 <= offset < len(t) => xy = t & + len(x) = offset & + (-1 = indexof(y, s, 0) => -1 = i) & + (indexof(y, s, 0) >= 0 => indexof(t, s, 0) + offset = i) + + if offset < 0 under specified optional lemmas: - (len(s) > len(t) -> i = -1) - (len(s) <= len(t) -> i <= len(t)-len(s)) + (len(s) > len(t) -> i = -1) + (len(s) <= len(t) -> i <= len(t)-len(s)) */ void theory_seq::add_indexof_axiom(expr* i) { expr* s, *t, *offset; rational r; VERIFY(m_util.str.is_index(i, t, s, offset)); - expr_ref emp(m), minus_one(m), zero(m), xsy(m); - minus_one = m_autil.mk_int(-1); - zero = m_autil.mk_int(0); - literal offset_ne_zero = null_literal; - bool is_num = m_autil.is_numeral(offset, r); - if (is_num && r.is_zero()) { - offset_ne_zero = null_literal; - } - else { - offset_ne_zero = ~mk_eq(offset, zero, false); - } - if (!is_num || r.is_zero()) { - expr_ref x = mk_skolem(m_contains_left, t, s); - expr_ref y = mk_skolem(m_contains_right, t, s); - xsy = m_util.str.mk_concat(x,s,y); - literal cnt = mk_literal(m_util.str.mk_contains(t, s)); - literal eq_empty = mk_eq_empty(s); - add_axiom(offset_ne_zero, cnt, mk_eq(i, minus_one, false)); - add_axiom(offset_ne_zero, ~eq_empty, mk_eq(i, zero, false)); - add_axiom(offset_ne_zero, ~cnt, eq_empty, mk_eq(t, xsy, false)); - tightest_prefix(s, x, ~cnt, offset_ne_zero); - } - if (is_num && r.is_zero()) { - return; - } + expr_ref minus_one(m_autil.mk_int(-1), m); + expr_ref zero(m_autil.mk_int(0), m); + expr_ref xsy(m); + // offset >= len(t) => indexof(s, t, offset) = -1 expr_ref len_t(m_util.str.mk_length(t), m); literal offset_ge_len = mk_literal(m_autil.mk_ge(mk_sub(offset, len_t), zero)); add_axiom(offset_ge_len, mk_eq(i, minus_one, false)); - // 0 <= offset & offset < len(t) => t = xy - // 0 <= offset & offset < len(t) => len(x) = offset - // 0 <= offset & offset < len(t) & ~contains(s, y) => indexof(t, s, offset) = -1 - // 0 <= offset & offset < len(t) & contains(s, y) => index(t, s, offset) = indexof(y, s, 0) + len(t) - expr_ref x = mk_skolem(m_indexof_left, t, s, offset); - expr_ref y = mk_skolem(m_indexof_right, t, s, offset); - expr_ref indexof(m_util.str.mk_index(y, s, zero), m); - // TBD: - //literal offset_ge_0 = mk_literal(m_autil.mk_ge(offset, zero)); - //add_axiom(~offset_ge_0, offset_ge_len, mk_eq(indexof, i, false)); - //add_axiom(~offset_ge_0, offset_ge_len, mk_eq(m_util.str.mk_length(x), offset, false)); - //add_axiom(~offset_ge_0, offset_ge_len, mk_eq(t, m_util.str.mk_concat(x, y), false)); + if (m_autil.is_numeral(offset, r) && r.is_zero()) { + expr_ref x = mk_skolem(m_contains_left, t, s); + expr_ref y = mk_skolem(m_contains_right, t, s); + xsy = m_util.str.mk_concat(x,s,y); + literal cnt = mk_literal(m_util.str.mk_contains(t, s)); + literal eq_empty = mk_eq_empty(s); + add_axiom(cnt, mk_eq(i, minus_one, false)); + add_axiom(~eq_empty, mk_eq(i, zero, false)); + add_axiom(~cnt, eq_empty, mk_eq(t, xsy, false)); + tightest_prefix(s, x, ~cnt); + } + else { + expr_ref x = mk_skolem(m_indexof_left, t, s, offset); + expr_ref y = mk_skolem(m_indexof_right, t, s, offset); + expr_ref indexof0(m_util.str.mk_index(y, s, zero), m); + expr_ref offset_p_indexof0(m_autil.mk_add(offset, indexof0), m); + literal offset_ge_0 = mk_literal(m_autil.mk_ge(offset, zero)); + + // 0 <= offset & offset < len(t) => t = xy + // 0 <= offset & offset < len(t) => len(x) = offset + // 0 <= offset & offset < len(t) & -1 = indexof(y,s,0) = -1 => -1 = i + // 0 <= offset & offset < len(t) & indexof(y,s,0) >= 0 = -1 => + // -1 = indexof(y,s,0) + offset = indexof(t, s, offset) + + add_axiom(~offset_ge_0, offset_ge_len, mk_eq(t, m_util.str.mk_concat(x, y), false)); + add_axiom(~offset_ge_0, offset_ge_len, mk_eq(m_util.str.mk_length(x), offset, false)); + add_axiom(~offset_ge_0, offset_ge_len, + ~mk_eq(indexof0, minus_one, false), mk_eq(i, minus_one, false)); + add_axiom(~offset_ge_0, offset_ge_len, + ~mk_literal(m_autil.mk_ge(indexof0, zero)), + mk_eq(offset_p_indexof0, i, false)); + } } /* @@ -1306,10 +1260,11 @@ void theory_seq::add_elim_string_axiom(expr* n) { /* let n = len(x) - - len(x) >= 0 - len(x) = 0 => x = "" - x = "" => len(x) = 0 + - len(a ++ b) = len(a) + len(b) if x = a ++ b + - len(unit(u)) = 1 if x = unit(u) + - len(str) = str.length() if x = str + - len(empty) = 0 if x = empty + - len(x) >= 0 otherwise */ void theory_seq::add_length_axiom(expr* n) { expr* x; @@ -1326,13 +1281,7 @@ void theory_seq::add_length_axiom(expr* n) { } } else { - expr_ref zero(m_autil.mk_int(0), m); - add_axiom(mk_literal(m_autil.mk_ge(n, zero))); - - //expr_ref emp(m_util.str.mk_empty(m.get_sort(x)), m); - //literal eq1(mk_eq(zero, n, false)); - //literal eq2(mk_eq(x, emp, false)); - //add_axiom(~eq1, eq2); + add_axiom(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); } } @@ -1364,7 +1313,6 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { eautomaton* a = get_automaton(e2); if (!a) return; - // if (m_util.str.is_empty(e1)) return; context& ctx = get_context(); @@ -1564,8 +1512,17 @@ void theory_seq::propagate_step(bool_var v, expr* step) { VERIFY(is_step(step, s, idx, re, i, j, t)); expr_ref nth = mk_nth(s, idx); propagate_eq(v, t, nth); + literal lit(v); + SASSERT(ctx.get_assignment(lit) == l_true); + propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); } +/* + lit => len(s) > 0 +*/ +void theory_seq::propagate_non_empty(literal lit, expr* s) { + propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), m_autil.mk_int(0)))); +} literal theory_seq::mk_literal(expr* _e) { expr_ref e(_e, m); @@ -1574,15 +1531,18 @@ literal theory_seq::mk_literal(expr* _e) { return ctx.get_literal(e); } +literal theory_seq::mk_equals(expr* a, expr* b) { + literal lit = mk_eq(a, b, false); + get_context().force_phase(lit); + return lit; +} + literal theory_seq::mk_eq_empty(expr* _e) { expr_ref e(_e, m); - context& ctx = get_context(); SASSERT(m_util.is_seq(e)); expr_ref emp(m); emp = m_util.str.mk_empty(m.get_sort(e)); - literal lit = mk_eq(e, emp, false); - ctx.force_phase(lit); - return lit; + return mk_equals(e, emp); } void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4) { @@ -1612,7 +1572,7 @@ bool theory_seq::is_skolem(symbol const& s, expr* e) const { } -void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { +void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs) { context& ctx = get_context(); enode* n1 = ensure_enode(e1); @@ -1622,10 +1582,18 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2) { } ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); + if (add_to_eqs) { + SASSERT(l_true == ctx.get_assignment(v)); + expr_ref l(e1, m), r(e2, m); + enode* m1 = ensure_enode(ctx.bool_var2expr(v)); + enode* m2 = ctx.get_enode(m.mk_true()); + enode_pair_dependency* deps = m_dm.mk_leaf(enode_pair(m1, m2)); + m_eqs.push_back(eq(l, r, deps)); + } + literal lit(v); TRACE("seq", tout << mk_pp(ctx.bool_var2expr(v), m) << " => " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); - literal lit(v); justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( @@ -1641,58 +1609,74 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { expr* e1, *e2; expr_ref f(m); - if (is_true && m_util.str.is_prefix(e, e1, e2)) { - f = mk_skolem(m_prefix, e1, e2); - f = m_util.str.mk_concat(e1, f); - propagate_eq(v, f, e2); + if (m_util.str.is_prefix(e, e1, e2)) { + if (is_true) { + f = mk_skolem(m_prefix, e1, e2); + f = m_util.str.mk_concat(e1, f); + propagate_eq(v, f, e2, true); + } + else { + // !prefix(e1,e2) => len(e1) > 0; + propagate_non_empty(literal(v, true), e1); + add_atom(e); + } } - else if (is_true && m_util.str.is_suffix(e, e1, e2)) { - f = mk_skolem(m_suffix, e1, e2); - f = m_util.str.mk_concat(f, e1); - propagate_eq(v, f, e2); + else if (m_util.str.is_suffix(e, e1, e2)) { + if (is_true) { + f = mk_skolem(m_suffix, e1, e2); + f = m_util.str.mk_concat(f, e1); + propagate_eq(v, f, e2, true); + } + else { + propagate_non_empty(literal(v, true), e1); + add_atom(e); + } } - else if (is_true && m_util.str.is_contains(e, e1, e2)) { - expr_ref f1 = mk_skolem(m_contains_left, e1, e2); - expr_ref f2 = mk_skolem(m_contains_right, e1, e2); - f = m_util.str.mk_concat(f1, m_util.str.mk_concat(e2, f2)); - propagate_eq(v, f, e1); + else if (m_util.str.is_contains(e, e1, e2)) { + if (is_true) { + expr_ref f1 = mk_skolem(m_contains_left, e1, e2); + expr_ref f2 = mk_skolem(m_contains_right, e1, e2); + f = m_util.str.mk_concat(f1, e2, f2); + propagate_eq(v, f, e1, true); + } + else { + literal lit(v, true); + propagate_non_empty(lit, e2); + propagate_lit(0, 1, &lit, ~mk_literal(m_util.str.mk_prefix(e2, e1))); + add_atom(e); + } } else if (is_accept(e)) { if (is_true) { - m_trail_stack.push(push_back_vector >(m_accepts)); - m_accepts.push_back(e); + propagate_acc_rej_length(v, e); + add_atom(e); } } else if (is_reject(e)) { if (is_true) { - m_trail_stack.push(push_back_vector >(m_rejects)); - m_rejects.push_back(e); + propagate_acc_rej_length(v, e); + add_atom(e); } } else if (is_step(e)) { if (is_true) { propagate_step(v, e); - m_trail_stack.push(push_back_vector >(m_steps)); - m_steps.push_back(e); + add_atom(e); } } else if (m_util.str.is_in_re(e)) { propagate_in_re(e, is_true); } else { - SASSERT(!is_true); - //if (m_util.str.is_prefix(e, e1, e2)) { - // could add negative prefix axioms: - // len(e1) <= len(e2) => e2 = seq.prefix.left(e2)*seq.prefix.right(e2) - // & len(seq.prefix.left(e2)) = len(e1) - // & seq.prefix.left(e2) != e1 - // or could solve prefix/suffix disunification constraints. - //} - m_trail_stack.push(push_back_vector(m_ineqs)); - m_ineqs.push_back(e); + UNREACHABLE(); } } +void theory_seq::add_atom(expr* e) { + m_trail_stack.push(push_back_vector >(m_atoms)); + m_atoms.push_back(e); +} + void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); @@ -1843,11 +1827,27 @@ expr_ref theory_seq::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned return expr_ref(m_util.mk_skolem(m_aut_step, args.size(), args.c_ptr(), m.mk_bool_sort()), m); } +/* + acc(s, idx, re, i) -> len(s) >= idx + rej(s, idx, re, i) => len(s) >= idx +*/ +void theory_seq::propagate_acc_rej_length(bool_var v, expr* e) { + context& ctx = get_context(); + expr *s, * idx, *re; + unsigned src; + eautomaton* aut = 0; + VERIFY(is_accept(e, s, idx, re, src, aut) || + is_reject(e, s, idx, re, src, aut)); + if (m_util.str.is_length(idx)) return; + SASSERT(m_autil.is_numeral(idx)); + literal lit(v); + SASSERT(ctx.get_assignment(lit) == l_true); + propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); +} /** acc(s, idx, re, i) -> \/ step(s, idx, re, i, j, t) if i is non-final acc(s, idx, re, i) -> len(s) <= idx \/ step(s, idx, re, i, j, t) if i is final - acc(s, idx, re, i) -> len(s) >= idx */ void theory_seq::add_accept2step(expr* acc) { context& ctx = get_context(); @@ -1902,7 +1902,6 @@ void theory_seq::add_step2accept(expr* step) { /* rej(s, idx, re, i) & nth(s,idx) = t & idx < len(s) => rej(s, idx + 1 re, j) - rej(s, idx, re, i) => idx <= len(s) */ void theory_seq::add_reject2reject(expr* rej) { context& ctx = get_context(); @@ -1928,27 +1927,180 @@ void theory_seq::add_reject2reject(expr* rej) { } } +/* + !prefix -> e2 = emp \/ nth(e1,0) != nth(e2,0) \/ !prefix(tail(e1),tail(e2)) +*/ +bool theory_seq::add_prefix2prefix(expr* e) { + context& ctx = get_context(); + expr* e1, *e2; + VERIFY(m_util.str.is_prefix(e, e1, e2)); + SASSERT(ctx.get_assignment(e) == l_false); + if (canonizes(false, e)) { + return false; + } + expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); + switch (assume_equality(e2, emp)) { + case l_true: + return false; // done + case l_undef: + return true; // retry + default: + break; + } + expr_ref head1(m), tail1(m), head2(m), tail2(m); + mk_decompose(e1, head1, tail1); + mk_decompose(e2, head2, tail2); + + literal lit = mk_eq(head1, head2, false); + switch (ctx.get_assignment(lit)) { + case l_true: { + literal_vector lits; + lits.push_back(~ctx.get_literal(e)); + lits.push_back(~mk_eq(e2, emp, false)); + lits.push_back(lit); + propagate_lit(0, lits.size(), lits.c_ptr(), ~mk_literal(m_util.str.mk_prefix(tail1, tail2))); + return false; + } + case l_false: + return false; + case l_undef: + ctx.force_phase(~lit); + return true; + } + return true; +} + +/* + !suffix(e1, e2) -> e2 = emp \/ nth(e1,len(e1)-1) != nth(e2,len(e2)-1) \/ !suffix(first(e1), first(e2)) + */ +bool theory_seq::add_suffix2suffix(expr* e) { + context& ctx = get_context(); + expr* e1, *e2; + VERIFY(m_util.str.is_suffix(e, e1, e2)); + SASSERT(ctx.get_assignment(e) == l_false); + if (canonizes(false, e)) { + return false; + } + + expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); + + switch (assume_equality(e2, emp)) { + case l_true: + return false; // done + case l_undef: + return true; // retry + case l_false: + break; + } + + NOT_IMPLEMENTED_YET(); + // TBD: + expr_ref head1(m), tail1(m), head2(m), tail2(m); + mk_decompose(e2, head2, tail2); + + literal lit = mk_eq(head1, head2, false); + switch (ctx.get_assignment(lit)) { + case l_true: { + literal_vector lits; + lits.push_back(~ctx.get_literal(e)); + lits.push_back(~mk_eq(e2, emp, false)); + lits.push_back(lit); + propagate_lit(0, lits.size(), lits.c_ptr(), mk_literal(m_util.str.mk_suffix(tail1, tail2))); + return false; + } + case l_false: + return false; + case l_undef: + ctx.force_phase(~lit); + return true; + } + return true; +} + +bool theory_seq::canonizes(bool sign, expr* e) { + context& ctx = get_context(); + enode_pair_dependency* deps = 0; + expr_ref cont = canonize(e, deps); + if ((m.is_true(cont) && !sign) || + (m.is_false(cont) && sign)) { + propagate_lit(deps, 0, 0, ctx.get_literal(e)); + return true; + } + if ((m.is_false(cont) && !sign) || + (m.is_true(cont) && sign)) { + return true; + } + return false; +} + +/* + !contains(e1, e2) -> !prefix(e2, e1) + !contains(e1, e2) -> e1 = emp \/ !contains(tail(e1), e2) + */ + +bool theory_seq::add_contains2contains(expr* e) { + context& ctx = get_context(); + expr* e1, *e2; + VERIFY(m_util.str.is_contains(e, e1, e2)); + SASSERT(ctx.get_assignment(e) == l_false); + if (canonizes(false, e)) { + return false; + } + expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); + + switch (assume_equality(e1, emp)) { + case l_true: + return false; // done + case l_undef: + return true; // retry + default: + break; + } + expr_ref head(m), tail(m); + mk_decompose(e1, head, tail); + literal lits[2] = { ~ctx.get_literal(e), ~mk_eq(e1, emp, false) }; + propagate_lit(0, 2, lits, ~mk_literal(m_util.str.mk_contains(tail, e1))); + return false; +} + bool theory_seq::propagate_automata() { context& ctx = get_context(); - bool change = false; - if (m_accepts_qhead < m_accepts.size()) - m_trail_stack.push(value_trail(m_accepts_qhead)), change = true; - if (m_rejects_qhead < m_rejects.size()) - m_trail_stack.push(value_trail(m_rejects_qhead)), change = true; - if (m_steps_qhead < m_steps.size()) - m_trail_stack.push(value_trail(m_steps_qhead)), change = true; - - while (m_accepts_qhead < m_accepts.size() && !ctx.inconsistent()) { - add_accept2step(m_accepts[m_accepts_qhead]); - ++m_accepts_qhead; + if (m_atoms_qhead == m_atoms.size()) { + return false; } - while (m_rejects_qhead < m_rejects.size() && !ctx.inconsistent()) { - add_reject2reject(m_rejects[m_rejects_qhead]); - ++m_rejects_qhead; + m_trail_stack.push(value_trail(m_atoms_qhead)); + ptr_vector re_add; + while (m_atoms_qhead < m_atoms.size() && !ctx.inconsistent()) { + expr* e = m_atoms[m_atoms_qhead]; + TRACE("seq", tout << mk_pp(e, m) << "\n";); + bool reQ = false; + if (is_accept(e)) { + add_accept2step(e); + } + else if (is_reject(e)) { + add_reject2reject(e); + } + else if (is_step(e)) { + add_step2accept(e); + } + else if (m_util.str.is_prefix(e)) { + reQ = add_prefix2prefix(e); + } + else if (m_util.str.is_suffix(e)) { + reQ = add_suffix2suffix(e); + } + else if (m_util.str.is_contains(e)) { + reQ = add_contains2contains(e); + } + if (reQ) { + re_add.push_back(e); + m_atoms[m_atoms_qhead] = m_atoms.back(); + m_atoms.pop_back(); + } + else { + ++m_atoms_qhead; + } } - while (m_steps_qhead < m_steps.size() && !ctx.inconsistent()) { - add_step2accept(m_steps[m_steps_qhead]); - ++m_steps_qhead; - } - return change || ctx.inconsistent(); + m_atoms.append(re_add); + return true; } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 5760fb437..798dd1341 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -252,7 +252,6 @@ namespace smt { unsigned m_branch_variable; unsigned m_solve_nqs; unsigned m_solve_eqs; - unsigned m_check_ineqs; }; ast_manager& m; enode_pair_dependency_manager m_dm; @@ -261,7 +260,6 @@ namespace smt { scoped_vector m_nqs; // set of current disequalities. seq_factory* m_factory; // value factory - expr_ref_vector m_ineqs; // inequalities to check solution against exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. obj_hashtable m_axiom_set; @@ -283,8 +281,8 @@ namespace smt { // maintain automata with regular expressions. scoped_ptr_vector m_automata; obj_map m_re2aut; - ptr_vector m_accepts, m_rejects, m_steps; - unsigned m_accepts_qhead, m_rejects_qhead, m_steps_qhead; + ptr_vector m_atoms; + unsigned m_atoms_qhead; virtual final_check_status final_check_eh(); virtual bool internalize_atom(app* atom, bool) { return internalize_term(atom); } @@ -309,14 +307,12 @@ namespace smt { virtual void init_model(model_generator & mg); // final check - bool check_ineqs(); // check if inequalities are violated. bool simplify_and_solve_eqs(); // solve unitary equalities bool branch_variable(); // branch on a variable bool split_variable(); // split a variable bool is_solved(); bool check_length_coherence(); bool propagate_length_coherence(expr* e); - bool check_ineq_coherence(); bool pre_process_eqs(bool simplify_or_solve, bool& propagated); bool simplify_eqs(bool& propagated) { return pre_process_eqs(true, propagated); } @@ -332,11 +328,11 @@ namespace smt { void propagate_lit(enode_pair_dependency* dep, literal lit) { propagate_lit(dep, 0, 0, lit); } void propagate_lit(enode_pair_dependency* dep, unsigned n, literal const* lits, literal lit); void propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2); - void propagate_eq(bool_var v, expr* e1, expr* e2); + void propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs = false); void set_conflict(enode_pair_dependency* dep, literal_vector const& lits = literal_vector()); bool find_branch_candidate(expr* l, expr_ref_vector const& rs); - bool assume_equality(expr* l, expr* r); + lbool assume_equality(expr* l, expr* r); // variable solving utilities bool occurs(expr* a, expr* b); @@ -368,6 +364,7 @@ namespace smt { void add_in_re_axiom(expr* n); literal mk_literal(expr* n); literal mk_eq_empty(expr* n); + literal mk_equals(expr* a, expr* b); void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal); expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); @@ -406,7 +403,14 @@ namespace smt { void add_reject2reject(expr* rej); void add_accept2step(expr* acc); void add_step2accept(expr* step); + bool add_prefix2prefix(expr* e); + bool add_suffix2suffix(expr* e); + bool add_contains2contains(expr* e); + bool canonizes(bool sign, expr* e); + void propagate_non_empty(literal lit, expr* s); + void propagate_acc_rej_length(bool_var v, expr* acc_rej); bool propagate_automata(); + void add_atom(expr* e); // diagnostics void display_equations(std::ostream& out) const; From 78550ec816129e9b8184618f09b6750b7107cf29 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 31 Dec 2015 07:48:14 -0800 Subject: [PATCH 20/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 8 +- src/ast/seq_decl_plugin.cpp | 2 +- src/smt/theory_seq.cpp | 177 +++++++++++++++++++----------- src/smt/theory_seq.h | 64 ++++++----- 4 files changed, 152 insertions(+), 99 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 77baa36fc..00b4371d9 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -475,6 +475,10 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { for (; i < as.size() && i < bs.size(); ++i) { all_values &= m().is_value(as[i].get()) && m().is_value(bs[i].get()); if (as[i].get() != bs[i].get()) { + if (all_values) { + result = m().mk_false(); + return BR_DONE; + } break; } }; @@ -483,10 +487,6 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { return BR_DONE; } SASSERT(i < as.size()); - if (all_values && (i < bs.size() || m_util.str.is_unit(as[i+1].get()))) { - result = m().mk_false(); - return BR_DONE; - } if (i == bs.size()) { expr_ref_vector es(m()); for (unsigned j = i; j < as.size(); ++j) { diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 57ccd1a3e..70ee4298b 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -611,7 +611,7 @@ app* seq_decl_plugin::mk_string(zstring const& s) { bool seq_decl_plugin::is_value(app* e) const { return - is_app_of(e, m_family_id, OP_STRING_CONST) || + is_app_of(e, m_family_id, OP_SEQ_EMPTY) || (is_app_of(e, m_family_id, OP_SEQ_UNIT) && m_manager->is_value(e->get_arg(0))); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index b9886a609..4f9273322 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -39,12 +39,12 @@ struct display_expr { -void theory_seq::solution_map::update(expr* e, expr* r, enode_pair_dependency* d) { +void theory_seq::solution_map::update(expr* e, expr* r, dependency* d) { if (e == r) { return; } m_cache.reset(); - std::pair value; + std::pair value; if (m_map.find(e, value)) { add_trail(DEL, e, value.first, value.second); } @@ -54,7 +54,7 @@ void theory_seq::solution_map::update(expr* e, expr* r, enode_pair_dependency* d add_trail(INS, e, r, d); } -void theory_seq::solution_map::add_trail(map_update op, expr* l, expr* r, enode_pair_dependency* d) { +void theory_seq::solution_map::add_trail(map_update op, expr* l, expr* r, dependency* d) { m_updates.push_back(op); m_lhs.push_back(l); m_rhs.push_back(r); @@ -65,8 +65,8 @@ bool theory_seq::solution_map::is_root(expr* e) const { return !m_map.contains(e); } -expr* theory_seq::solution_map::find(expr* e, enode_pair_dependency*& d) { - std::pair value; +expr* theory_seq::solution_map::find(expr* e, dependency*& d) { + std::pair value; d = 0; expr* result = e; while (m_map.find(result, value)) { @@ -80,7 +80,7 @@ expr* theory_seq::solution_map::find(expr* e, enode_pair_dependency*& d) { } expr* theory_seq::solution_map::find(expr* e) { - std::pair value; + std::pair value; while (m_map.find(e, value)) { e = value.first; } @@ -258,11 +258,15 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { return false; } + + bool all_units = true; + expr_ref_vector cases(m); expr_ref v0(m), v(m); v0 = m_util.str.mk_empty(m.get_sort(l)); if (l_false != assume_equality(l, v0)) { return true; } + cases.push_back(v0); for (unsigned j = 0; j < rs.size(); ++j) { if (occurs(l, rs[j])) { return false; @@ -276,12 +280,23 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { return true; } } + all_units = false; } + all_units &= m_util.str.is_unit(rs[j]); v0 = (j == 0)? rs[0] : m_util.str.mk_concat(v0, rs[j]); + cases.push_back(v0); if (l_false != assume_equality(l, v0)) { return true; } } + if (all_units) { + literal_vector lits; + for (unsigned i = 0; i < cases.size(); ++i) { + lits.push_back(mk_eq(l, cases[i].get(), false)); + } + get_context().mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + return true; + } return false; } @@ -328,7 +343,6 @@ bool theory_seq::propagate_length_coherence(expr* e) { tout << " lo: " << lo << " hi: " << hi << "\n"; ); - literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); expr_ref seq(e, m); expr_ref_vector elems(m); unsigned _lo = lo.get_unsigned(); @@ -337,25 +351,24 @@ bool theory_seq::propagate_length_coherence(expr* e) { elems.push_back(head); seq = tail; } + expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); elems.push_back(seq); tail = m_util.str.mk_concat(elems.size(), elems.c_ptr()); // len(e) >= low => e = tail - add_axiom(~low, mk_eq(e, tail, false)); - assume_equality(tail, e); + literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); + add_axiom(~low, mk_eq(e, tail, false)); + assume_equality(seq, emp); if (upper_bound(e, hi)) { expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); } - //m_replay_length_coherence.push_back(e); - //m_replay_length_coherence_qhead = m_replay_length_coherence.size(); return true; } bool theory_seq::check_length_coherence() { - if (m_length.empty()) return true; context& ctx = get_context(); bool coherent = true; obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); @@ -366,10 +379,10 @@ bool theory_seq::check_length_coherence() { expr_ref head(m), tail(m); if (!propagate_length_coherence(e) && l_false == assume_equality(e, emp)) { - mk_decompose(e, head, tail); // e = emp \/ e = unit(head.elem(e))*tail(e) + mk_decompose(e, head, tail); expr_ref conc(m_util.str.mk_concat(head, tail), m); - add_axiom(mk_eq_empty(e), mk_eq(e, conc, false)); + propagate_is_conc(e, conc); assume_equality(tail, emp); } return false; @@ -378,6 +391,24 @@ bool theory_seq::check_length_coherence() { return coherent; } +/* + lit => s != "" +*/ +void theory_seq::propagate_non_empty(literal lit, expr* s) { + SASSERT(get_context().get_assignment(lit) == l_true); + propagate_lit(0, 1, &lit, ~mk_eq_empty(s)); +} + +void theory_seq::propagate_is_conc(expr* e, expr* conc) { + TRACE("seq", tout << mk_pp(conc, m) << " is non-empty\n";); + context& ctx = get_context(); + literal lit = ~mk_eq_empty(e); + SASSERT(ctx.get_assignment(lit) == l_true); + propagate_lit(0, 1, &lit, mk_eq(e, conc, false)); + expr_ref e1(e, m), e2(conc, m); + m_eqs.push_back(eq(e1, e2, m_dm.mk_leaf(assumption(lit)))); +} + expr_ref theory_seq::mk_nth(expr* s, expr* idx) { sort* char_sort = 0; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); @@ -435,36 +466,55 @@ bool theory_seq::is_solved() { return true; } -void theory_seq::propagate_lit(enode_pair_dependency* dep, unsigned n, literal const* lits, literal lit) { +void theory_seq::linearize(dependency* dep, enode_pair_vector& eqs, literal_vector& lits) const { + svector assumptions; + const_cast(m_dm).linearize(dep, assumptions); + for (unsigned i = 0; i < assumptions.size(); ++i) { + assumption const& a = assumptions[i]; + if (a.lit != null_literal) { + lits.push_back(a.lit); + } + if (a.n1 != 0) { + eqs.push_back(enode_pair(a.n1, a.n2)); + } + } +} + + + +void theory_seq::propagate_lit(dependency* dep, unsigned n, literal const* _lits, literal lit) { context& ctx = get_context(); ctx.mark_as_relevant(lit); - vector _eqs; - m_dm.linearize(dep, _eqs); + literal_vector lits(n, _lits); + enode_pair_vector eqs; + linearize(dep, eqs, lits); TRACE("seq", ctx.display_detailed_literal(tout, lit); - tout << " <- "; ctx.display_literals_verbose(tout, n, lits); display_deps(tout, dep);); + tout << " <- "; ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); display_deps(tout, dep);); justification* js = ctx.mk_justification( ext_theory_propagation_justification( - get_id(), ctx.get_region(), n, lits, _eqs.size(), _eqs.c_ptr(), lit)); + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), lit)); ctx.assign(lit, js); } -void theory_seq::set_conflict(enode_pair_dependency* dep, literal_vector const& lits) { +void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { context& ctx = get_context(); - vector _eqs; - m_dm.linearize(dep, _eqs); + enode_pair_vector eqs; + literal_vector lits(_lits); + linearize(dep, eqs, lits); TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); display_deps(tout, dep); ;); ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( - get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), _eqs.size(), _eqs.c_ptr(), 0, 0))); + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), 0, 0))); } -void theory_seq::propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2) { +void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { context& ctx = get_context(); - vector _eqs; - m_dm.linearize(dep, _eqs); + literal_vector lits; + enode_pair_vector eqs; + linearize(dep, eqs, lits); TRACE("seq", tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << " <- "; display_deps(tout, dep); @@ -472,13 +522,13 @@ void theory_seq::propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2) justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( - get_id(), ctx.get_region(), 0, 0, _eqs.size(), _eqs.c_ptr(), n1, n2)); + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), n1, n2)); ctx.assign_eq(n1, n2, eq_justification(js)); } -bool theory_seq::simplify_eq(expr* l, expr* r, enode_pair_dependency* deps, bool& propagated) { +bool theory_seq::simplify_eq(expr* l, expr* r, dependency* deps, bool& propagated) { context& ctx = get_context(); seq_rewriter rw(m); expr_ref_vector lhs(m), rhs(m); @@ -518,7 +568,7 @@ bool theory_seq::simplify_eq(expr* l, expr* r, enode_pair_dependency* deps, bool return true; } -bool theory_seq::solve_unit_eq(expr* l, expr* r, enode_pair_dependency* deps, bool& propagated) { +bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps, bool& propagated) { expr_ref lh = canonize(l, deps); expr_ref rh = canonize(r, deps); if (lh == rh) { @@ -571,7 +621,7 @@ bool theory_seq::is_nth(expr* e) const { return is_skolem(m_nth, e); } -bool theory_seq::add_solution(expr* l, expr* r, enode_pair_dependency* deps) { +bool theory_seq::add_solution(expr* l, expr* r, dependency* deps) { if (l == r) { return false; } @@ -645,7 +695,7 @@ bool theory_seq::solve_ne(unsigned idx) { } for (unsigned i = 0; i < n.m_lhs.size(); ++i) { expr_ref_vector lhs(m), rhs(m); - enode_pair_dependency* deps = 0; + dependency* deps = 0; expr* l = n.m_lhs[i]; expr* r = n.m_rhs[i]; expr_ref lh = canonize(l, deps); @@ -744,16 +794,15 @@ bool theory_seq::internalize_term(app* term) { ctx.set_var_theory(bv, get_id()); ctx.mark_as_relevant(bv); } - else { - enode* e = 0; - if (ctx.e_internalized(term)) { - e = ctx.get_enode(term); - } - else { - e = ctx.mk_enode(term, false, m.is_bool(term), true); - } - mk_var(e); + enode* e = 0; + if (ctx.e_internalized(term)) { + e = ctx.get_enode(term); } + else { + e = ctx.mk_enode(term, false, m.is_bool(term), true); + } + mk_var(e); + return true; } @@ -846,13 +895,15 @@ void theory_seq::display_disequation(std::ostream& out, ne const& e) const { display_deps(out, e.m_dep); } -void theory_seq::display_deps(std::ostream& out, enode_pair_dependency* dep) const { - vector _eqs; - const_cast(m_dm).linearize(dep, _eqs); - for (unsigned i = 0; i < _eqs.size(); ++i) { - out << " " << mk_pp(_eqs[i].first->get_owner(), m) << " = " << mk_pp(_eqs[i].second->get_owner(), m); +void theory_seq::display_deps(std::ostream& out, dependency* dep) const { + literal_vector lits; + enode_pair_vector eqs; + linearize(dep, eqs, lits); + for (unsigned i = 0; i < eqs.size(); ++i) { + out << " " << mk_pp(eqs[i].first->get_owner(), m) << " = " << mk_pp(eqs[i].second->get_owner(), m); } out << "\n"; + get_context().display_literals_verbose(tout, lits.size(), lits.c_ptr()); } void theory_seq::collect_statistics(::statistics & st) const { @@ -953,7 +1004,7 @@ app* theory_seq::mk_value(app* e) { expr* e1; expr_ref result(e, m); if (m_util.str.is_unit(e, e1)) { - enode_pair_dependency* deps = 0; + dependency* deps = 0; result = expand(e1, deps); bv_util bv(m); rational val; @@ -1027,15 +1078,15 @@ bool theory_seq::can_propagate() { return m_axioms_head < m_axioms.size(); } -expr_ref theory_seq::canonize(expr* e, enode_pair_dependency*& eqs) { +expr_ref theory_seq::canonize(expr* e, dependency*& eqs) { expr_ref result = expand(e, eqs); m_rewrite(result); return result; } -expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { +expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { expr_ref result(m); - enode_pair_dependency* deps = 0; + dependency* deps = 0; expr_dep ed; if (m_rep.find_cache(e0, ed)) { eqs = m_dm.mk_join(eqs, ed.second); @@ -1073,9 +1124,9 @@ expr_ref theory_seq::expand(expr* e0, enode_pair_dependency*& eqs) { return result; } -void theory_seq::add_dependency(enode_pair_dependency*& dep, enode* a, enode* b) { +void theory_seq::add_dependency(dependency*& dep, enode* a, enode* b) { if (a != b) { - dep = m_dm.mk_join(dep, m_dm.mk_leaf(std::make_pair(a, b))); + dep = m_dm.mk_join(dep, m_dm.mk_leaf(assumption(a, b))); } } @@ -1517,12 +1568,6 @@ void theory_seq::propagate_step(bool_var v, expr* step) { propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); } -/* - lit => len(s) > 0 -*/ -void theory_seq::propagate_non_empty(literal lit, expr* s) { - propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), m_autil.mk_int(0)))); -} literal theory_seq::mk_literal(expr* _e) { expr_ref e(_e, m); @@ -1585,9 +1630,7 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs) { if (add_to_eqs) { SASSERT(l_true == ctx.get_assignment(v)); expr_ref l(e1, m), r(e2, m); - enode* m1 = ensure_enode(ctx.bool_var2expr(v)); - enode* m2 = ctx.get_enode(m.mk_true()); - enode_pair_dependency* deps = m_dm.mk_leaf(enode_pair(m1, m2)); + dependency* deps = m_dm.mk_leaf(assumption(literal(v))); m_eqs.push_back(eq(l, r, deps)); } literal lit(v); @@ -1616,7 +1659,7 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { propagate_eq(v, f, e2, true); } else { - // !prefix(e1,e2) => len(e1) > 0; + // !prefix(e1,e2) => e1 != "" propagate_non_empty(literal(v, true), e1); add_atom(e); } @@ -1639,9 +1682,9 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { f = m_util.str.mk_concat(f1, e2, f2); propagate_eq(v, f, e1, true); } - else { + else if (!canonizes(false, e)) { literal lit(v, true); - propagate_non_empty(lit, e2); + propagate_non_empty(literal(v, true), e2); propagate_lit(0, 1, &lit, ~mk_literal(m_util.str.mk_prefix(e2, e1))); add_atom(e); } @@ -1680,11 +1723,11 @@ void theory_seq::add_atom(expr* e) { void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); - if (n1 != n2) { + if (n1 != n2 && m_util.is_seq(n1->get_owner())) { expr_ref o1(n1->get_owner(), m); expr_ref o2(n2->get_owner(), m); TRACE("seq", tout << o1 << " = " << o2 << "\n";); - enode_pair_dependency* deps = m_dm.mk_leaf(enode_pair(n1, n2)); + dependency* deps = m_dm.mk_leaf(assumption(n1, n2)); bool propagated = false; if (!simplify_eq(o1, o2, deps, propagated)) { m_eqs.push_back(eq(o1, o2, deps)); @@ -1709,6 +1752,7 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { if (!m.is_false(eq)) { m_nqs.push_back(ne(e1, e2)); } + // add solution for variable that is non-empty? } void theory_seq::push_scope_eh() { @@ -2019,8 +2063,9 @@ bool theory_seq::add_suffix2suffix(expr* e) { bool theory_seq::canonizes(bool sign, expr* e) { context& ctx = get_context(); - enode_pair_dependency* deps = 0; + dependency* deps = 0; expr_ref cont = canonize(e, deps); + TRACE("seq", tout << mk_pp(e, m) << " -> " << cont << "\n";); if ((m.is_true(cont) && !sign) || (m.is_false(cont) && sign)) { propagate_lit(deps, 0, 0, ctx.get_literal(e)); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 798dd1341..2de6a36d0 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -32,11 +32,17 @@ Revision History: namespace smt { class theory_seq : public theory { - typedef scoped_dependency_manager enode_pair_dependency_manager; - typedef enode_pair_dependency_manager::dependency enode_pair_dependency; - + struct assumption { + enode* n1, *n2; + literal lit; + assumption(enode* n1, enode* n2): n1(n1), n2(n2), lit(null_literal) {} + assumption(literal lit): n1(0), n2(0), lit(lit) {} + }; + typedef scoped_dependency_manager dependency_manager; + typedef dependency_manager::dependency dependency; + typedef trail_stack th_trail_stack; - typedef std::pair expr_dep; + typedef std::pair expr_dep; typedef obj_map eqdep_map_t; // cache to track evaluations under equalities @@ -55,26 +61,26 @@ namespace smt { class solution_map { enum map_update { INS, DEL }; ast_manager& m; - enode_pair_dependency_manager& m_dm; + dependency_manager& m_dm; eqdep_map_t m_map; eval_cache m_cache; expr_ref_vector m_lhs, m_rhs; - ptr_vector m_deps; + ptr_vector m_deps; svector m_updates; unsigned_vector m_limit; - void add_trail(map_update op, expr* l, expr* r, enode_pair_dependency* d); + void add_trail(map_update op, expr* l, expr* r, dependency* d); public: - solution_map(ast_manager& m, enode_pair_dependency_manager& dm): + solution_map(ast_manager& m, dependency_manager& dm): m(m), m_dm(dm), m_cache(m), m_lhs(m), m_rhs(m) {} bool empty() const { return m_map.empty(); } - void update(expr* e, expr* r, enode_pair_dependency* d); + void update(expr* e, expr* r, dependency* d); void add_cache(expr* v, expr_dep& r) { m_cache.insert(v, r); } bool find_cache(expr* v, expr_dep& r) { return m_cache.find(v, r); } - expr* find(expr* e, enode_pair_dependency*& d); + expr* find(expr* e, dependency*& d); expr* find(expr* e); bool is_root(expr* e) const; - void cache(expr* e, expr* r, enode_pair_dependency* d); + void cache(expr* e, expr* r, dependency* d); void reset_cache() { m_cache.reset(); } void push_scope() { m_limit.push_back(m_updates.size()); } void pop_scope(unsigned num_scopes); @@ -103,8 +109,8 @@ namespace smt { struct eq { expr_ref m_lhs; expr_ref m_rhs; - enode_pair_dependency* m_dep; - eq(expr_ref& l, expr_ref& r, enode_pair_dependency* d): + dependency* m_dep; + eq(expr_ref& l, expr_ref& r, dependency* d): m_lhs(l), m_rhs(r), m_dep(d) {} eq(eq const& other): m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_dep(other.m_dep) {} eq& operator=(eq const& other) { m_lhs = other.m_lhs; m_rhs = other.m_rhs; m_dep = other.m_dep; return *this; } @@ -118,7 +124,7 @@ namespace smt { expr_ref_vector m_lhs; expr_ref_vector m_rhs; literal_vector m_lits; - enode_pair_dependency* m_dep; + dependency* m_dep; ne(expr_ref& l, expr_ref& r): m_solved(false), m_l(l), m_r(r), m_lhs(l.get_manager()), m_rhs(r.get_manager()), m_dep(0) { m_lhs.push_back(l); @@ -229,10 +235,10 @@ namespace smt { }; class push_dep : public trail { - enode_pair_dependency* m_dep; + dependency* m_dep; unsigned m_idx; public: - push_dep(theory_seq& th, unsigned idx, enode_pair_dependency* d): m_dep(th.m_nqs[idx].m_dep), m_idx(idx) { + push_dep(theory_seq& th, unsigned idx, dependency* d): m_dep(th.m_nqs[idx].m_dep), m_idx(idx) { th.m_nqs.ref(idx).m_dep = d; } virtual void undo(theory_seq& th) { @@ -254,7 +260,7 @@ namespace smt { unsigned m_solve_eqs; }; ast_manager& m; - enode_pair_dependency_manager m_dm; + dependency_manager m_dm; solution_map m_rep; // unification representative. scoped_vector m_eqs; // set of current equations. scoped_vector m_nqs; // set of current disequalities. @@ -317,19 +323,20 @@ namespace smt { bool pre_process_eqs(bool simplify_or_solve, bool& propagated); bool simplify_eqs(bool& propagated) { return pre_process_eqs(true, propagated); } bool solve_basic_eqs(bool& propagated) { return pre_process_eqs(false, propagated); } - bool simplify_eq(expr* l, expr* r, enode_pair_dependency* dep, bool& propagated); - bool solve_unit_eq(expr* l, expr* r, enode_pair_dependency* dep, bool& propagated); + bool simplify_eq(expr* l, expr* r, dependency* dep, bool& propagated); + bool solve_unit_eq(expr* l, expr* r, dependency* dep, bool& propagated); bool solve_nqs(); bool solve_ne(unsigned i); bool unchanged(expr* e, expr_ref_vector& es) const { return es.size() == 1 && es[0] == e; } // asserting consequences - void propagate_lit(enode_pair_dependency* dep, literal lit) { propagate_lit(dep, 0, 0, lit); } - void propagate_lit(enode_pair_dependency* dep, unsigned n, literal const* lits, literal lit); - void propagate_eq(enode_pair_dependency* dep, enode* n1, enode* n2); + void linearize(dependency* dep, enode_pair_vector& eqs, literal_vector& lits) const; + void propagate_lit(dependency* dep, literal lit) { propagate_lit(dep, 0, 0, lit); } + void propagate_lit(dependency* dep, unsigned n, literal const* lits, literal lit); + void propagate_eq(dependency* dep, enode* n1, enode* n2); void propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs = false); - void set_conflict(enode_pair_dependency* dep, literal_vector const& lits = literal_vector()); + void set_conflict(dependency* dep, literal_vector const& lits = literal_vector()); bool find_branch_candidate(expr* l, expr_ref_vector const& rs); lbool assume_equality(expr* l, expr* r); @@ -337,12 +344,12 @@ namespace smt { // variable solving utilities bool occurs(expr* a, expr* b); bool is_var(expr* b); - bool add_solution(expr* l, expr* r, enode_pair_dependency* dep); + bool add_solution(expr* l, expr* r, dependency* dep); bool is_nth(expr* a) const; expr_ref mk_nth(expr* s, expr* idx); - expr_ref canonize(expr* e, enode_pair_dependency*& eqs); - expr_ref expand(expr* e, enode_pair_dependency*& eqs); - void add_dependency(enode_pair_dependency*& dep, enode* a, enode* b); + expr_ref canonize(expr* e, dependency*& eqs); + expr_ref expand(expr* e, dependency*& eqs); + void add_dependency(dependency*& dep, enode* a, enode* b); void get_concat(expr* e, ptr_vector& concats); @@ -408,6 +415,7 @@ namespace smt { bool add_contains2contains(expr* e); bool canonizes(bool sign, expr* e); void propagate_non_empty(literal lit, expr* s); + void propagate_is_conc(expr* e, expr* conc); void propagate_acc_rej_length(bool_var v, expr* acc_rej); bool propagate_automata(); void add_atom(expr* e); @@ -416,7 +424,7 @@ namespace smt { void display_equations(std::ostream& out) const; void display_disequations(std::ostream& out) const; void display_disequation(std::ostream& out, ne const& e) const; - void display_deps(std::ostream& out, enode_pair_dependency* deps) const; + void display_deps(std::ostream& out, dependency* deps) const; public: theory_seq(ast_manager& m); virtual ~theory_seq(); From 6c6d1d92c4d5cc7da5963abb7bf3142a72849f5f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 31 Dec 2015 16:10:41 -0800 Subject: [PATCH 21/35] seq Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 315 +++++++++++++++++++++++++++++------------ src/smt/theory_seq.h | 12 +- 2 files changed, 231 insertions(+), 96 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 4f9273322..954baa4c6 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -245,6 +245,14 @@ bool theory_seq::branch_variable() { m_branch_variable_head = k; return true; } +#if 0 + if (!has_length(e.m_lhs)) { + enforce_length(ensure_enode(e.m_lhs)); + } + if (!has_length(e.m_rhs)) { + enforce_length(ensure_enode(e.m_rhs)); + } +#endif } return ctx.inconsistent(); } @@ -258,7 +266,6 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { return false; } - bool all_units = true; expr_ref_vector cases(m); expr_ref v0(m), v(m); @@ -280,7 +287,6 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { return true; } } - all_units = false; } all_units &= m_util.str.is_unit(rs[j]); v0 = (j == 0)? rs[0] : m_util.str.mk_concat(v0, rs[j]); @@ -289,14 +295,18 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { return true; } } +#if 0 if (all_units) { literal_vector lits; for (unsigned i = 0; i < cases.size(); ++i) { lits.push_back(mk_eq(l, cases[i].get(), false)); } + lits.push_back(~mk_eq(e1, e2, false)); get_context().mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); return true; } +#endif + return false; } @@ -354,22 +364,20 @@ bool theory_seq::propagate_length_coherence(expr* e) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); elems.push_back(seq); tail = m_util.str.mk_concat(elems.size(), elems.c_ptr()); - // len(e) >= low => e = tail + // len(e) >= low => e = tail; literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); add_axiom(~low, mk_eq(e, tail, false)); assume_equality(seq, emp); if (upper_bound(e, hi)) { - expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); - expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); + expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); + expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); } - return true; } bool theory_seq::check_length_coherence() { - context& ctx = get_context(); bool coherent = true; obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); for (; it != end; ++it) { @@ -406,7 +414,8 @@ void theory_seq::propagate_is_conc(expr* e, expr* conc) { SASSERT(ctx.get_assignment(lit) == l_true); propagate_lit(0, 1, &lit, mk_eq(e, conc, false)); expr_ref e1(e, m), e2(conc, m); - m_eqs.push_back(eq(e1, e2, m_dm.mk_leaf(assumption(lit)))); + new_eq_eh(m_dm.mk_leaf(assumption(lit)), ctx.get_enode(e1), ctx.get_enode(e2)); + } expr_ref theory_seq::mk_nth(expr* s, expr* idx) { @@ -415,6 +424,13 @@ expr_ref theory_seq::mk_nth(expr* s, expr* idx) { return mk_skolem(m_nth, s, idx, 0, char_sort); } +expr_ref theory_seq::mk_last(expr* s) { + sort* char_sort = 0; + VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); + return mk_skolem(m_seq_last, s, 0, 0, char_sort); +} + + void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { expr* e1, *e2; zstring s; @@ -511,6 +527,9 @@ void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { } void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { + if (n1->get_root() == n2->get_root()) { + return; + } context& ctx = get_context(); literal_vector lits; enode_pair_vector eqs; @@ -524,6 +543,19 @@ void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { ext_theory_eq_propagation_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), n1, n2)); ctx.assign_eq(n1, n2, eq_justification(js)); + + enforce_length_coherence(n1, n2); +} + +void theory_seq::enforce_length_coherence(enode* n1, enode* n2) { + expr* o1 = n1->get_owner(); + expr* o2 = n2->get_owner(); + if (has_length(o1) && !has_length(o2)) { + enforce_length(n2); + } + else if (has_length(o2) && !has_length(o1)) { + enforce_length(n1); + } } @@ -628,12 +660,14 @@ bool theory_seq::add_solution(expr* l, expr* r, dependency* deps) { context& ctx = get_context(); TRACE("seq", tout << mk_pp(l, m) << " ==> " << mk_pp(r, m) << "\n";); m_rep.update(l, r, deps); - if (ctx.e_internalized(l) && ctx.e_internalized(r) && ctx.get_enode(l)->get_root() != ctx.get_enode(r)->get_root()) { - propagate_eq(deps, ctx.get_enode(l), ctx.get_enode(r)); - return true; + enode* n1 = ensure_enode(l); + enode* n2 = ensure_enode(r); + if (n1->get_root() == n2->get_root()) { + return false; } else { - return false; + propagate_eq(deps, n1, n2); + return true; } } @@ -900,16 +934,20 @@ void theory_seq::display_deps(std::ostream& out, dependency* dep) const { enode_pair_vector eqs; linearize(dep, eqs, lits); for (unsigned i = 0; i < eqs.size(); ++i) { - out << " " << mk_pp(eqs[i].first->get_owner(), m) << " = " << mk_pp(eqs[i].second->get_owner(), m); + out << "\n " << mk_pp(eqs[i].first->get_owner(), m) << " = " << mk_pp(eqs[i].second->get_owner(), m); + } + for (unsigned i = 0; i < lits.size(); ++i) { + literal lit = lits[i]; + get_context().display_literals_verbose(out << "\n ", 1, &lit); } out << "\n"; - get_context().display_literals_verbose(tout, lits.size(), lits.c_ptr()); + } void theory_seq::collect_statistics(::statistics & st) const { st.update("seq num splits", m_stats.m_num_splits); st.update("seq num reductions", m_stats.m_num_reductions); - st.update("e", m_stats.m_propagate_automata); + st.update("seq unfold def", m_stats.m_propagate_automata); st.update("seq length coherence", m_stats.m_check_length_coherence); st.update("seq branch", m_stats.m_branch_variable); st.update("seq solve !=", m_stats.m_solve_nqs); @@ -1156,7 +1194,7 @@ void theory_seq::deque_axiom(expr* n) { if (m_util.str.is_length(n)) { add_length_axiom(n); } - else if (m_util.str.is_empty(n) && !has_length(n)) { + else if (m_util.str.is_empty(n) && !has_length(n) && !m_length.empty()) { enforce_length(get_context().get_enode(n)); } else if (m_util.str.is_index(n)) { @@ -1181,20 +1219,16 @@ void theory_seq::deque_axiom(expr* n) { encode that s is not a proper prefix of xs1 where s1 is all of s, except the last element. - lit or s = "" or s = s1*c - lit or s = "" or len(c) = 1 + lit or s = "" or s = s1*(unit c) lit or s = "" or !prefix(s, x*s1) */ void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) { expr_ref s1 = mk_skolem(m_seq_first, s); - expr_ref c = mk_skolem(m_seq_last, s); - expr_ref s1c(m_util.str.mk_concat(s1, c), m); - expr_ref lc(m_util.str.mk_length(c), m); - expr_ref one(m_autil.mk_int(1), m); + expr_ref c = mk_last(s); + expr_ref s1c(m_util.str.mk_concat(s1, m_util.str.mk_unit(c)), m); literal s_eq_emp = mk_eq_empty(s); add_axiom(lit1, lit2, s_eq_emp, mk_eq(s, s1c, false)); - add_axiom(lit1, lit2, s_eq_emp, mk_eq(lc, one, false)); - add_axiom(lit1, lit2, s_eq_emp, ~mk_literal(m_util.str.mk_contains(s, m_util.str.mk_concat(x, s1)))); + add_axiom(lit1, lit2, s_eq_emp, ~mk_literal(m_util.str.mk_prefix(s, m_util.str.mk_concat(x, s1)))); } /* @@ -1414,9 +1448,10 @@ enode* theory_seq::ensure_enode(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { ctx.internalize(e, false); - ctx.mark_as_relevant(ctx.get_enode(e)); } - return ctx.get_enode(e); + enode* n = ctx.get_enode(e); + ctx.mark_as_relevant(n); + return n; } static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { @@ -1555,19 +1590,56 @@ void theory_seq::add_at_axiom(expr* e) { } /** - step(s, idx, re, i, j, t) -> nth(s, idx) == t + step(s, idx, re, i, j, t) -> nth(s, idx) == t & len(s) > idx */ -void theory_seq::propagate_step(bool_var v, expr* step) { +void theory_seq::propagate_step(literal lit, expr* step) { context& ctx = get_context(); expr* re, *t, *s, *idx, *i, *j; VERIFY(is_step(step, s, idx, re, i, j, t)); expr_ref nth = mk_nth(s, idx); - propagate_eq(v, t, nth); - literal lit(v); + propagate_eq(lit, t, nth); SASSERT(ctx.get_assignment(lit) == l_true); - propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); + propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); + ensure_nth(lit, s, idx); } +/* + lit => s = (nth s 0) ++ (nth s 1) ++ ... ++ (nth s idx) ++ (tail s idx) +*/ +void theory_seq::ensure_nth(literal lit, expr* s, expr* idx) { + context& ctx = get_context(); + rational r; + VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); + unsigned _idx = r.get_unsigned(); + dependency* dep = 0; + expr_ref s1 = canonize(s, dep); + ptr_vector es; + expr* e1; + expr_ref nth = mk_nth(s, idx); + expr_ref head(m), tail(m), conc(m); + expr_ref_vector elems(m); + get_concat(s1, es); + unsigned i = 0; + for (; i < _idx && i < es.size() && m_util.str.is_unit(es[i]); ++i) {}; + if (i == _idx && i < es.size() && m_util.str.is_unit(es[i], e1)) { + dep = m_dm.mk_join(dep, m_dm.mk_leaf(assumption(lit))); + propagate_eq(dep, ensure_enode(nth), ensure_enode(e1)); + return; + } + // TBD could tune this aggregate quadratic overhead + expr* s2 = s; + for (unsigned j = 0; j <= _idx; ++j) { + mk_decompose(s2, head, tail); + elems.push_back(head); + s2 = tail; + } + elems.push_back(s2); + conc = m_util.str.mk_concat(elems.size(), elems.c_ptr()); + propagate_eq(lit, s, conc, true); + + // TBD: examine other places for enforcing constraints on tail + add_axiom(~lit, mk_eq(m_util.str.mk_length(s), m_util.str.mk_length(conc), false)); +} literal theory_seq::mk_literal(expr* _e) { expr_ref e(_e, m); @@ -1617,7 +1689,7 @@ bool theory_seq::is_skolem(symbol const& s, expr* e) const { } -void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs) { +void theory_seq::propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs) { context& ctx = get_context(); enode* n1 = ensure_enode(e1); @@ -1628,14 +1700,13 @@ void theory_seq::propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs) { ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); if (add_to_eqs) { - SASSERT(l_true == ctx.get_assignment(v)); - expr_ref l(e1, m), r(e2, m); - dependency* deps = m_dm.mk_leaf(assumption(literal(v))); - m_eqs.push_back(eq(l, r, deps)); + SASSERT(l_true == ctx.get_assignment(lit)); + dependency* deps = m_dm.mk_leaf(assumption(lit)); + new_eq_eh(deps, n1, n2); + } - literal lit(v); TRACE("seq", - tout << mk_pp(ctx.bool_var2expr(v), m) << " => " + tout << mk_pp(ctx.bool_var2expr(lit.var()), m) << " => " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); justification* js = ctx.mk_justification( @@ -1651,16 +1722,17 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { expr* e = ctx.bool_var2expr(v); expr* e1, *e2; expr_ref f(m); + literal lit(v, !is_true); if (m_util.str.is_prefix(e, e1, e2)) { if (is_true) { f = mk_skolem(m_prefix, e1, e2); f = m_util.str.mk_concat(e1, f); - propagate_eq(v, f, e2, true); + propagate_eq(lit, f, e2, true); } else { // !prefix(e1,e2) => e1 != "" - propagate_non_empty(literal(v, true), e1); + propagate_non_empty(lit, e1); add_atom(e); } } @@ -1668,10 +1740,18 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { if (is_true) { f = mk_skolem(m_suffix, e1, e2); f = m_util.str.mk_concat(f, e1); - propagate_eq(v, f, e2, true); + propagate_eq(lit, f, e2, true); } else { - propagate_non_empty(literal(v, true), e1); + // lit => e1 != empty + propagate_non_empty(lit, e1); + + // lit => e1 = first ++ (unit last) + expr_ref f1 = mk_skolem(m_seq_first, e1); + expr_ref f2 = mk_last(e1); + f = m_util.str.mk_concat(f1, m_util.str.mk_unit(f2)); + propagate_eq(lit, e1, f, true); + add_atom(e); } } @@ -1680,30 +1760,29 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { expr_ref f1 = mk_skolem(m_contains_left, e1, e2); expr_ref f2 = mk_skolem(m_contains_right, e1, e2); f = m_util.str.mk_concat(f1, e2, f2); - propagate_eq(v, f, e1, true); + propagate_eq(lit, f, e1, true); } else if (!canonizes(false, e)) { - literal lit(v, true); - propagate_non_empty(literal(v, true), e2); + propagate_non_empty(lit, e2); propagate_lit(0, 1, &lit, ~mk_literal(m_util.str.mk_prefix(e2, e1))); add_atom(e); } } else if (is_accept(e)) { if (is_true) { - propagate_acc_rej_length(v, e); + propagate_acc_rej_length(lit, e); add_atom(e); } } else if (is_reject(e)) { if (is_true) { - propagate_acc_rej_length(v, e); + propagate_acc_rej_length(lit, e); add_atom(e); } } else if (is_step(e)) { if (is_true) { - propagate_step(v, e); + propagate_step(lit, e); add_atom(e); } } @@ -1723,21 +1802,20 @@ void theory_seq::add_atom(expr* e) { void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { enode* n1 = get_enode(v1); enode* n2 = get_enode(v2); + dependency* deps = m_dm.mk_leaf(assumption(n1, n2)); + new_eq_eh(deps, n1, n2); +} + +void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { if (n1 != n2 && m_util.is_seq(n1->get_owner())) { expr_ref o1(n1->get_owner(), m); expr_ref o2(n2->get_owner(), m); TRACE("seq", tout << o1 << " = " << o2 << "\n";); - dependency* deps = m_dm.mk_leaf(assumption(n1, n2)); bool propagated = false; if (!simplify_eq(o1, o2, deps, propagated)) { m_eqs.push_back(eq(o1, o2, deps)); } - if (has_length(o1) && !has_length(o2)) { - enforce_length(n2); - } - else if (has_length(o2) && !has_length(o1)) { - enforce_length(n1); - } + enforce_length_coherence(n1, n2); } } @@ -1872,21 +1950,32 @@ expr_ref theory_seq::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned } /* - acc(s, idx, re, i) -> len(s) >= idx - rej(s, idx, re, i) => len(s) >= idx + acc(s, idx, re, i) -> len(s) >= idx if i is final + rej(s, idx, re, i) -> len(s) >= idx if i is non-final + + acc(s, idx, re, i) -> len(s) > idx if i is non-final + rej(s, idx, re, i) -> len(s) > idx if i is final */ -void theory_seq::propagate_acc_rej_length(bool_var v, expr* e) { +void theory_seq::propagate_acc_rej_length(literal lit, expr* e) { context& ctx = get_context(); expr *s, * idx, *re; unsigned src; eautomaton* aut = 0; - VERIFY(is_accept(e, s, idx, re, src, aut) || - is_reject(e, s, idx, re, src, aut)); + bool is_acc; + is_acc = is_accept(e, s, idx, re, src, aut); + if (!is_acc) { + VERIFY(is_reject(e, s, idx, re, src, aut)); + } if (m_util.str.is_length(idx)) return; SASSERT(m_autil.is_numeral(idx)); - literal lit(v); SASSERT(ctx.get_assignment(lit) == l_true); - propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); + bool is_final = aut->is_final_state(src); + if (is_final == is_acc) { + propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); + } + else { + propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); + } } /** @@ -1945,30 +2034,70 @@ void theory_seq::add_step2accept(expr* step) { /* - rej(s, idx, re, i) & nth(s,idx) = t & idx < len(s) => rej(s, idx + 1 re, j) + rej(s, idx, re, i) & nth(s, idx) = t & idx < len(s) => rej(s, idx + 1, re, j) + + len(s) > idx -> s = (nth 0 s) ++ .. ++ (nth idx s) ++ (tail idx s) + +Recall we also have: + rej(s, idx, re, i) -> len(s) >= idx if i is non-final + rej(s, idx, re, i) -> len(s) > idx if i is final + */ -void theory_seq::add_reject2reject(expr* rej) { +bool theory_seq::add_reject2reject(expr* rej) { context& ctx = get_context(); SASSERT(ctx.get_assignment(rej) == l_true); - expr* e, *idx, *re; + expr* s, *idx, *re; unsigned src; rational r; eautomaton* aut = 0; - VERIFY(is_reject(rej, e, idx, re, src, aut)); - if (!aut || m_util.str.is_length(idx)) return; + VERIFY(is_reject(rej, s, idx, re, src, aut)); + if (!aut || m_util.str.is_length(idx)) return false; VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); expr_ref idx1(m_autil.mk_int(r.get_unsigned() + 1), m); eautomaton::moves mvs; aut->get_moves_from(src, mvs); literal rej1 = ctx.get_literal(rej); - expr_ref len(m_util.str.mk_length(e), m); - add_axiom(~rej1, mk_literal(m_autil.mk_ge(len, idx))); + expr_ref len(m_util.str.mk_length(s), m); + literal len_le_idx = mk_literal(m_autil.mk_le(len, idx)); + switch (ctx.get_assignment(len_le_idx)) { + case l_true: + return false; + case l_undef: + ctx.force_phase(len_le_idx); + return true; + default: + break; + } + expr_ref nth = mk_nth(s, idx); + ensure_nth(~len_le_idx, s, idx); + literal_vector eqs; + bool has_undef = false; for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move const& mv = mvs[i]; - expr_ref nth = mk_nth(e, idx); - literal rej2 = mk_reject(e, idx1, re, m_autil.mk_int(mv.dst())); - add_axiom(~rej1, ~mk_eq(nth, mv.t(), false), ~mk_literal(m_autil.mk_ge(len, idx)), rej2); + literal eq = mk_eq(nth, mv.t(), false); + switch (ctx.get_assignment(eq)) { + case l_false: + case l_true: + break; + case l_undef: + ctx.force_phase(~eq); + has_undef = true; + break; + } + eqs.push_back(eq); } + if (has_undef) { + return true; + } + for (unsigned i = 0; i < mvs.size(); ++i) { + eautomaton::move const& mv = mvs[i]; + literal eq = eqs[i]; + if (ctx.get_assignment(eq) == l_true) { + literal rej2 = mk_reject(s, idx1, re, m_autil.mk_int(mv.dst())); + add_axiom(~rej1, ~eq, len_le_idx, rej2); + } + } + return false; } /* @@ -2015,7 +2144,7 @@ bool theory_seq::add_prefix2prefix(expr* e) { } /* - !suffix(e1, e2) -> e2 = emp \/ nth(e1,len(e1)-1) != nth(e2,len(e2)-1) \/ !suffix(first(e1), first(e2)) + !suffix(e1, e2) -> e2 = emp \/ last(e1) != last(e2) \/ !suffix(first(e1), first(e2)) */ bool theory_seq::add_suffix2suffix(expr* e) { context& ctx = get_context(); @@ -2032,33 +2161,35 @@ bool theory_seq::add_suffix2suffix(expr* e) { case l_true: return false; // done case l_undef: + ctx.force_phase(mk_eq(e2, emp, false)); return true; // retry case l_false: break; } - - NOT_IMPLEMENTED_YET(); - // TBD: - expr_ref head1(m), tail1(m), head2(m), tail2(m); - mk_decompose(e2, head2, tail2); + expr_ref first2 = mk_skolem(m_seq_first, e2); + expr_ref last2 = mk_last(e2); + expr_ref first1 = mk_skolem(m_seq_first, e1); + expr_ref last1 = mk_last(e1); + expr_ref conc(m_util.str.mk_concat(first2, m_util.str.mk_unit(last2)), m); + propagate_eq(~mk_eq(e2, emp, false), e2, conc); - literal lit = mk_eq(head1, head2, false); - switch (ctx.get_assignment(lit)) { - case l_true: { - literal_vector lits; - lits.push_back(~ctx.get_literal(e)); - lits.push_back(~mk_eq(e2, emp, false)); - lits.push_back(lit); - propagate_lit(0, lits.size(), lits.c_ptr(), mk_literal(m_util.str.mk_suffix(tail1, tail2))); - return false; - } + literal last_eq = mk_eq(last1, last2, false); + switch (ctx.get_assignment(last_eq)) { case l_false: - return false; + return false; // done case l_undef: - ctx.force_phase(~lit); + ctx.force_phase(~last_eq); return true; + case l_true: + break; } - return true; + + literal_vector lits; + lits.push_back(~ctx.get_literal(e)); + lits.push_back(~mk_eq(e2, emp, false)); + lits.push_back(last_eq); + propagate_lit(0, lits.size(), lits.c_ptr(), ~mk_literal(m_util.str.mk_suffix(first1, first2))); + return false; } bool theory_seq::canonizes(bool sign, expr* e) { @@ -2123,7 +2254,7 @@ bool theory_seq::propagate_automata() { add_accept2step(e); } else if (is_reject(e)) { - add_reject2reject(e); + reQ = add_reject2reject(e); } else if (is_step(e)) { add_step2accept(e); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 2de6a36d0..92c2ebc05 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -335,7 +335,7 @@ namespace smt { void propagate_lit(dependency* dep, literal lit) { propagate_lit(dep, 0, 0, lit); } void propagate_lit(dependency* dep, unsigned n, literal const* lits, literal lit); void propagate_eq(dependency* dep, enode* n1, enode* n2); - void propagate_eq(bool_var v, expr* e1, expr* e2, bool add_to_eqs = false); + void propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs = false); void set_conflict(dependency* dep, literal_vector const& lits = literal_vector()); bool find_branch_candidate(expr* l, expr_ref_vector const& rs); @@ -347,6 +347,7 @@ namespace smt { bool add_solution(expr* l, expr* r, dependency* dep); bool is_nth(expr* a) const; expr_ref mk_nth(expr* s, expr* idx); + expr_ref mk_last(expr* e); expr_ref canonize(expr* e, dependency*& eqs); expr_ref expand(expr* e, dependency*& eqs); void add_dependency(dependency*& dep, enode* a, enode* b); @@ -365,6 +366,7 @@ namespace smt { bool has_length(expr *e) const { return m_length.contains(e); } void add_length(expr* e); void enforce_length(enode* n); + void enforce_length_coherence(enode* n1, enode* n2); void add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); @@ -406,19 +408,21 @@ namespace smt { expr_ref mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t); bool is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const; bool is_step(expr* e) const; - void propagate_step(bool_var v, expr* n); - void add_reject2reject(expr* rej); + void propagate_step(literal lit, expr* n); + bool add_reject2reject(expr* rej); void add_accept2step(expr* acc); void add_step2accept(expr* step); bool add_prefix2prefix(expr* e); bool add_suffix2suffix(expr* e); bool add_contains2contains(expr* e); + void ensure_nth(literal lit, expr* s, expr* idx); bool canonizes(bool sign, expr* e); void propagate_non_empty(literal lit, expr* s); void propagate_is_conc(expr* e, expr* conc); - void propagate_acc_rej_length(bool_var v, expr* acc_rej); + void propagate_acc_rej_length(literal lit, expr* acc_rej); bool propagate_automata(); void add_atom(expr* e); + void new_eq_eh(dependency* dep, enode* n1, enode* n2); // diagnostics void display_equations(std::ostream& out) const; From 876fd1f7bac46ff95ad0a3c059cad425579a76df Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 1 Jan 2016 09:00:21 -0800 Subject: [PATCH 22/35] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 45 ++++++++++---- src/ast/seq_decl_plugin.cpp | 2 +- src/muz/rel/dl_mk_simple_joins.cpp | 2 + src/smt/theory_seq.cpp | 99 +++++++++++++++++++++++------- src/smt/theory_seq.h | 29 ++++++++- 5 files changed, 140 insertions(+), 37 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 00b4371d9..2db2391d9 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -520,20 +520,10 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { result = m().mk_eq(m_util.str.mk_empty(m().get_sort(a)), a); return BR_REWRITE3; } - // TBD concatenation is right-associative - expr* a1, *a2, *b1, *b2; - if (m_util.str.is_concat(a, a1, a2) && - m_util.str.is_concat(b, b1, b2) && a2 == b2) { - result = m_util.str.mk_suffix(a1, b1); - return BR_REWRITE1; - } - if (m_util.str.is_concat(b, b1, b2) && b2 == a) { - result = m().mk_true(); - return BR_DONE; - } + bool isc1 = false; bool isc2 = false; - + expr* a1, *a2, *b1, *b2; if (m_util.str.is_concat(a, a1, a2) && m_util.str.is_string(a2, s1)) { isc1 = true; } @@ -593,6 +583,37 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { } } } + expr_ref_vector as(m()), bs(m()); + m_util.str.get_concat(a, as); + m_util.str.get_concat(b, bs); + bool change = false; + while (as.size() > 0 && bs.size() > 0 && as.back() == bs.back()) { + as.pop_back(); + bs.pop_back(); + change = true; + } + if (as.size() > 0 && bs.size() > 0 && m().is_value(as.back()) && m().is_value(bs.back())) { + result = m().mk_false(); + return BR_DONE; + } + if (change) { + // suffix("", bs) <- true + if (as.empty()) { + result = m().mk_true(); + return BR_DONE; + } + // suffix(as, "") iff as = "" + if (bs.empty()) { + for (unsigned j = 0; j < as.size(); ++j) { + bs.push_back(m().mk_eq(m_util.str.mk_empty(m().get_sort(a)), as[j].get())); + } + result = mk_and(bs); + return BR_REWRITE3; + } + result = m_util.str.mk_suffix(m_util.str.mk_concat(as.size(), as.c_ptr()), + m_util.str.mk_concat(bs.size(), bs.c_ptr())); + return BR_DONE; + } return BR_FAILED; } diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 70ee4298b..c3f882e8d 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -25,7 +25,6 @@ Revision History: zstring::zstring(encoding enc): m_encoding(enc) {} zstring::zstring(char const* s, encoding enc): m_encoding(enc) { - // TBD: epply decoding while (*s) { m_buffer.push_back(*s); ++s; @@ -81,6 +80,7 @@ zstring zstring::replace(zstring const& src, zstring const& dst) const { return result; } +// TBD: SMT-LIB 2.5 strings don't have escape characters other than " static char* esc_table[32] = { "\\0", "^A", "^B", "^C", "^D", "^E", "^F", "\\a", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V","^W","^X","^Y","^Z","\\e","^\\","^]","^^","^_"}; diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index e76b3a25b..3f5c2b78f 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -273,6 +273,7 @@ namespace datalog { */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { SASSERT(t1!=t2); + std::cout << "insert: " << mk_pp(t1, m) << " - " << mk_pp(t2, m) << "\n"; cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); pair_info * & ptr_inf = e->get_data().m_value; if (ptr_inf==0) { @@ -306,6 +307,7 @@ namespace datalog { } void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { + std::cout << "remove: " << mk_pp(key.first, m) << " - " << mk_pp(key.second, m) << "\n"; pair_info * ptr = &get_pair(key); if (ptr->remove_rule(r, original_len)) { SASSERT(ptr->m_rules.empty()); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 954baa4c6..69ae25aba 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -150,6 +150,7 @@ void theory_seq::exclusion_table::display(std::ostream& out) const { } } + theory_seq::theory_seq(ast_manager& m): theory(m.mk_family_id("seq")), m(m), @@ -372,6 +373,7 @@ bool theory_seq::propagate_length_coherence(expr* e) { expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); + m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); } return true; @@ -1048,12 +1050,18 @@ app* theory_seq::mk_value(app* e) { rational val; unsigned sz; if (bv.is_numeral(result, val, sz) && sz == zstring().num_bits()) { - svector val_as_bits; - for (unsigned i = 0; i < sz; ++i) { - val_as_bits.push_back(!val.is_even()); - val = div(val, rational(2)); + unsigned v = val.get_unsigned(); + if ((0 <= v && v < 32) || v == 127) { + result = m_util.str.mk_unit(result); + } + else { + svector val_as_bits; + for (unsigned i = 0; i < sz; ++i) { + val_as_bits.push_back(1 == v % 2); + v = v / 2; + } + result = m_util.str.mk_string(zstring(sz, val_as_bits.c_ptr())); } - result = m_util.str.mk_string(zstring(sz, val_as_bits.c_ptr())); } else { result = m_util.str.mk_unit(result); @@ -1113,7 +1121,7 @@ theory_var theory_seq::mk_var(enode* n) { } bool theory_seq::can_propagate() { - return m_axioms_head < m_axioms.size(); + return m_axioms_head < m_axioms.size() || !m_replay.empty(); } expr_ref theory_seq::canonize(expr* e, dependency*& eqs) { @@ -1177,6 +1185,11 @@ void theory_seq::propagate() { deque_axiom(e); ++m_axioms_head; } + while (!m_replay.empty() && !ctx.inconsistent()) { + (*m_replay[m_replay.size()-1])(*this); + TRACE("seq", tout << "replay: " << ctx.get_scope_level() << "\n";); + m_replay.pop_back(); + } } void theory_seq::enque_axiom(expr* e) { @@ -1733,7 +1746,9 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { else { // !prefix(e1,e2) => e1 != "" propagate_non_empty(lit, e1); - add_atom(e); + if (add_prefix2prefix(e)) { + add_atom(e); + } } } else if (m_util.str.is_suffix(e, e1, e2)) { @@ -1752,7 +1767,9 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { f = m_util.str.mk_concat(f1, m_util.str.mk_unit(f2)); propagate_eq(lit, e1, f, true); - add_atom(e); + if (add_suffix2suffix(e)) { + add_atom(e); + } } } else if (m_util.str.is_contains(e, e1, e2)) { @@ -1765,13 +1782,17 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { else if (!canonizes(false, e)) { propagate_non_empty(lit, e2); propagate_lit(0, 1, &lit, ~mk_literal(m_util.str.mk_prefix(e2, e1))); - add_atom(e); + if (add_contains2contains(e)) { + add_atom(e); + } } } else if (is_accept(e)) { if (is_true) { propagate_acc_rej_length(lit, e); - add_atom(e); + if (add_accept2step(e)) { + add_atom(e); + } } } else if (is_reject(e)) { @@ -1783,7 +1804,9 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { else if (is_step(e)) { if (is_true) { propagate_step(lit, e); - add_atom(e); + if (add_step2accept(e)) { + add_atom(e); + } } } else if (m_util.str.is_in_re(e)) { @@ -1979,10 +2002,10 @@ void theory_seq::propagate_acc_rej_length(literal lit, expr* e) { } /** - acc(s, idx, re, i) -> \/ step(s, idx, re, i, j, t) if i is non-final + acc(s, idx, re, i) -> \/ step(s, idx, re, i, j, t) if i is non-final acc(s, idx, re, i) -> len(s) <= idx \/ step(s, idx, re, i, j, t) if i is final */ -void theory_seq::add_accept2step(expr* acc) { +bool theory_seq::add_accept2step(expr* acc) { context& ctx = get_context(); SASSERT(ctx.get_assignment(acc) == l_true); expr *e, * idx, *re; @@ -1990,7 +2013,9 @@ void theory_seq::add_accept2step(expr* acc) { unsigned src; eautomaton* aut = 0; VERIFY(is_accept(acc, e, idx, re, src, aut)); - if (!aut || m_util.str.is_length(idx)) return; + if (!aut || m_util.str.is_length(idx)) { + return false; + } SASSERT(m_autil.is_numeral(idx)); eautomaton::moves mvs; aut->get_moves_from(src, mvs); @@ -2000,18 +2025,25 @@ void theory_seq::add_accept2step(expr* acc) { lits.push_back(~ctx.get_literal(acc)); if (aut->is_final_state(src)) { lits.push_back(mk_literal(m_autil.mk_le(len, idx))); + if (ctx.get_assignment(lits.back()) == l_true) { + return false; + } } for (unsigned i = 0; i < mvs.size(); ++i) { eautomaton::move mv = mvs[i]; step = mk_step(e, idx, re, src, mv.dst(), mv.t()); lits.push_back(mk_literal(step)); + if (ctx.get_assignment(lits.back()) == l_true) { + return false; + } } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + //std::cout << lits << "\n"; for (unsigned i = 0; i < lits.size(); ++i) { // TBD ctx.mark_as_relevant(lits[i]); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); - add_axiom(~ctx.get_literal(acc), mk_literal(m_autil.mk_ge(len, idx))); + return false; } @@ -2019,17 +2051,40 @@ void theory_seq::add_accept2step(expr* acc) { acc(s, idx, re, i) & step(s, idx, re, i, j, t) => acc(s, idx + 1, re, j) */ -void theory_seq::add_step2accept(expr* step) { +bool theory_seq::add_step2accept(expr* step) { context& ctx = get_context(); SASSERT(ctx.get_assignment(step) == l_true); - rational r; expr* re, *t, *s, *idx, *i, *j; VERIFY(is_step(step, s, idx, re, i, j, t)); - VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); - expr_ref idx1(m_autil.mk_int(r.get_unsigned() + 1), m); literal acc1 = mk_accept(s, idx, re, i); - literal acc2 = mk_accept(s, idx1, re, j); - add_axiom(~acc1, ~ctx.get_literal(step), acc2); + switch (ctx.get_assignment(acc1)) { + case l_false: + break; + case l_undef: + return true; + case l_true: { + rational r; + VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); + expr_ref idx1(m_autil.mk_int(r.get_unsigned() + 1), m); + literal acc2 = mk_accept(s, idx1, re, j); + literal_vector lits; + lits.push_back(acc1); + lits.push_back(ctx.get_literal(step)); + lits.push_back(~acc2); + switch (ctx.get_assignment(acc2)) { + case l_undef: + propagate_lit(0, 2, lits.c_ptr(), acc2); + break; + case l_true: + break; + case l_false: + set_conflict(0, lits); + break; + } + break; + } + } + return false; } @@ -2257,7 +2312,7 @@ bool theory_seq::propagate_automata() { reQ = add_reject2reject(e); } else if (is_step(e)) { - add_step2accept(e); + reQ = add_step2accept(e); } else if (m_util.str.is_prefix(e)) { reQ = add_prefix2prefix(e); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 92c2ebc05..341c1eb07 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -246,6 +246,30 @@ namespace smt { } }; + class apply { + public: + virtual ~apply() {} + virtual void operator()(theory_seq& th) = 0; + }; + + class replay_length_coherence : public apply { + expr_ref m_e; + public: + replay_length_coherence(ast_manager& m, expr* e) : m_e(e, m) {} + virtual void operator()(theory_seq& th) { + th.propagate_length_coherence(m_e); + } + }; + + class push_replay : public trail { + apply* m_apply; + public: + push_replay(apply* app): m_apply(app) {} + virtual void undo(theory_seq& th) { + th.m_replay.push_back(m_apply); + } + }; + void erase_index(unsigned idx, unsigned i); struct stats { @@ -273,6 +297,7 @@ namespace smt { unsigned m_branch_variable_head; // index of first equation to examine. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. obj_hashtable m_length; // is length applied + scoped_ptr_vector m_replay; // set of actions to replay model_generator* m_mg; th_rewriter m_rewrite; seq_util m_util; @@ -410,8 +435,8 @@ namespace smt { bool is_step(expr* e) const; void propagate_step(literal lit, expr* n); bool add_reject2reject(expr* rej); - void add_accept2step(expr* acc); - void add_step2accept(expr* step); + bool add_accept2step(expr* acc); + bool add_step2accept(expr* step); bool add_prefix2prefix(expr* e); bool add_suffix2suffix(expr* e); bool add_contains2contains(expr* e); From 5e553a4dc163979f8f2effea1ae89d738b653051 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Jan 2016 13:32:44 -0800 Subject: [PATCH 23/35] seq Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 314 +++++++++++++++++++++++++++++------------ src/smt/theory_seq.h | 79 ++++++----- 2 files changed, 267 insertions(+), 126 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 69ae25aba..c271e405e 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -165,7 +165,9 @@ theory_seq::theory_seq(ast_manager& m): m_util(m), m_autil(m), m_trail_stack(*this), - m_atoms_qhead(0) { + m_atoms_qhead(0), + m_new_solution(false), + m_new_propagation(false) { m_prefix = "seq.prefix.suffix"; m_suffix = "seq.suffix.prefix"; m_contains_left = "seq.contains.left"; @@ -237,15 +239,26 @@ bool theory_seq::branch_variable() { ls.reset(); rs.reset(); m_util.str.get_concat(e.m_lhs, ls); m_util.str.get_concat(e.m_rhs, rs); - - if (!ls.empty() && find_branch_candidate(ls[0].get(), rs)) { + +#if 1 + if (!ls.empty() && find_branch_candidate(e.m_dep, ls[0].get(), rs)) { m_branch_variable_head = k; return true; } - if (!rs.empty() && find_branch_candidate(rs[0].get(), ls)) { + if (!rs.empty() && find_branch_candidate(e.m_dep, rs[0].get(), ls)) { m_branch_variable_head = k; return true; } +#else + if (ls.size() > 1 && find_branch_candidate(e.m_dep, ls.back(), rs)) { + m_branch_variable_head = k; + return true; + } + if (rs.size() > 1 && find_branch_candidate(e.m_dep, rs.back(), ls)) { + m_branch_variable_head = k; + return true; + } +#endif #if 0 if (!has_length(e.m_lhs)) { enforce_length(ensure_enode(e.m_lhs)); @@ -258,7 +271,7 @@ bool theory_seq::branch_variable() { return ctx.inconsistent(); } -bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { +bool theory_seq::find_branch_candidate(dependency* dep, expr* l, expr_ref_vector const& rs) { TRACE("seq", tout << mk_pp(l, m) << " " << (is_var(l)?"var":"not var") << "\n";); @@ -274,40 +287,27 @@ bool theory_seq::find_branch_candidate(expr* l, expr_ref_vector const& rs) { if (l_false != assume_equality(l, v0)) { return true; } - cases.push_back(v0); for (unsigned j = 0; j < rs.size(); ++j) { if (occurs(l, rs[j])) { return false; } - zstring s; - if (m_util.str.is_string(rs[j], s)) { - for (unsigned k = 1; k < s.length(); ++k) { - v = m_util.str.mk_string(s.extract(0, k)); - if (v0) v = m_util.str.mk_concat(v0, v); - if (l_false != assume_equality(l, v)) { - return true; - } - } - } + SASSERT(!m_util.str.is_string(rs[j])); all_units &= m_util.str.is_unit(rs[j]); - v0 = (j == 0)? rs[0] : m_util.str.mk_concat(v0, rs[j]); - cases.push_back(v0); + v0 = m_util.str.mk_concat(j + 1, rs.c_ptr()); if (l_false != assume_equality(l, v0)) { return true; } } -#if 0 if (all_units) { literal_vector lits; - for (unsigned i = 0; i < cases.size(); ++i) { - lits.push_back(mk_eq(l, cases[i].get(), false)); + lits.push_back(~mk_eq_empty(l)); + for (unsigned i = 0; i < rs.size(); ++i) { + v0 = m_util.str.mk_concat(i + 1, rs.c_ptr()); + lits.push_back(~mk_eq(l, v0, false)); } - lits.push_back(~mk_eq(e1, e2, false)); - get_context().mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + set_conflict(dep, lits); return true; } -#endif - return false; } @@ -417,7 +417,10 @@ void theory_seq::propagate_is_conc(expr* e, expr* conc) { propagate_lit(0, 1, &lit, mk_eq(e, conc, false)); expr_ref e1(e, m), e2(conc, m); new_eq_eh(m_dm.mk_leaf(assumption(lit)), ctx.get_enode(e1), ctx.get_enode(e2)); +} +bool theory_seq::is_nth(expr* e) const { + return is_skolem(m_nth, e); } expr_ref theory_seq::mk_nth(expr* s, expr* idx) { @@ -476,10 +479,14 @@ void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { bool theory_seq::is_solved() { if (!m_eqs.empty()) { + IF_VERBOSE(10, verbose_stream() << "(seq.giveup " << m_eqs[0].m_lhs << " = " << m_eqs[0].m_rhs << " is unsolved)\n";); return false; } for (unsigned i = 0; i < m_automata.size(); ++i) { - if (!m_automata[i]) return false; + if (!m_automata[i]) { + IF_VERBOSE(10, verbose_stream() << "(seq.giveup regular expression did not compile to automaton)\n";); + return false; + } } return true; } @@ -513,6 +520,7 @@ void theory_seq::propagate_lit(dependency* dep, unsigned n, literal const* _lits ext_theory_propagation_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), lit)); + m_new_propagation = true; ctx.assign(lit, js); } @@ -522,6 +530,7 @@ void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { literal_vector lits(_lits); linearize(dep, eqs, lits); TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); display_deps(tout, dep); ;); + m_new_propagation = true; ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( @@ -545,6 +554,7 @@ void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { ext_theory_eq_propagation_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), n1, n2)); ctx.assign_eq(n1, n2, eq_justification(js)); + m_new_propagation = true; enforce_length_coherence(n1, n2); } @@ -562,23 +572,17 @@ void theory_seq::enforce_length_coherence(enode* n1, enode* n2) { -bool theory_seq::simplify_eq(expr* l, expr* r, dependency* deps, bool& propagated) { +bool theory_seq::simplify_eq(expr* l, expr* r, dependency* deps) { context& ctx = get_context(); seq_rewriter rw(m); expr_ref_vector lhs(m), rhs(m); - expr_ref lh = canonize(l, deps); - expr_ref rh = canonize(r, deps); - if (!rw.reduce_eq(lh, rh, lhs, rhs)) { + if (!rw.reduce_eq(l, r, lhs, rhs)) { // equality is inconsistent. - TRACE("seq", tout << lh << " != " << rh << "\n";); + TRACE("seq", tout << mk_pp(l, m) << " != " << mk_pp(r, m) << "\n";); set_conflict(deps); - propagated = true; return true; } - if (unchanged(l, lhs) && unchanged(r, rhs)) { - return false; - } - if (unchanged(r, lhs) && unchanged(l, rhs)) { + if (unchanged(l, lhs, r, rhs)) { return false; } SASSERT(lhs.size() == rhs.size()); @@ -590,7 +594,6 @@ bool theory_seq::simplify_eq(expr* l, expr* r, dependency* deps, bool& propagate } else { propagate_eq(deps, ensure_enode(li), ensure_enode(ri)); - propagated = true; } } TRACE("seq", @@ -602,18 +605,14 @@ bool theory_seq::simplify_eq(expr* l, expr* r, dependency* deps, bool& propagate return true; } -bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps, bool& propagated) { - expr_ref lh = canonize(l, deps); - expr_ref rh = canonize(r, deps); - if (lh == rh) { +bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { + if (l == r) { return true; } - if (is_var(lh) && !occurs(lh, rh)) { - propagated = add_solution(lh, rh, deps) || propagated; + if (is_var(l) && !occurs(l, r) && add_solution(l, r, deps)) { return true; } - if (is_var(rh) && !occurs(rh, lh)) { - propagated = add_solution(rh, lh, deps) || propagated; + if (is_var(r) && !occurs(r, l) && add_solution(r, l, deps)) { return true; } @@ -651,38 +650,29 @@ bool theory_seq::is_var(expr* a) { } -bool theory_seq::is_nth(expr* e) const { - return is_skolem(m_nth, e); -} -bool theory_seq::add_solution(expr* l, expr* r, dependency* deps) { +bool theory_seq::add_solution(expr* l, expr* r, dependency* deps) { if (l == r) { return false; } context& ctx = get_context(); TRACE("seq", tout << mk_pp(l, m) << " ==> " << mk_pp(r, m) << "\n";); + m_new_solution = true; m_rep.update(l, r, deps); enode* n1 = ensure_enode(l); enode* n2 = ensure_enode(r); - if (n1->get_root() == n2->get_root()) { - return false; - } - else { + if (n1->get_root() != n2->get_root()) { propagate_eq(deps, n1, n2); - return true; } + return true; } - -bool theory_seq::pre_process_eqs(bool simplify_or_solve, bool& propagated) { +bool theory_seq::solve_eqs(unsigned i) { context& ctx = get_context(); bool change = false; - for (unsigned i = 0; !ctx.inconsistent() && i < m_eqs.size(); ++i) { + for (; !ctx.inconsistent() && i < m_eqs.size(); ++i) { eq e = m_eqs[i]; - - if (simplify_or_solve? - simplify_eq(e.m_lhs, e.m_rhs, e.m_dep, propagated): - solve_unit_eq(e.m_lhs, e.m_rhs, e.m_dep, propagated)) { + if (solve_eq(e.m_lhs, e.m_rhs, e.m_dep)) { if (i + 1 != m_eqs.size()) { eq e1 = m_eqs[m_eqs.size()-1]; m_eqs.set(i, e1); @@ -693,7 +683,123 @@ bool theory_seq::pre_process_eqs(bool simplify_or_solve, bool& propagated) { change = true; } } - return change; + return change || ctx.inconsistent(); +} + +bool theory_seq::solve_eq(expr* _l, expr* _r, dependency* deps) { + context& ctx = get_context(); + expr_ref l = canonize(_l, deps); + expr_ref r = canonize(_r, deps); + TRACE("seq", tout << l << " = " << r << "\n";); + if (!ctx.inconsistent() && simplify_eq(l, r, deps)) { + return true; + } + if (!ctx.inconsistent() && solve_unit_eq(l, r, deps)) { + return true; + } + if (!ctx.inconsistent() && solve_binary_eq(l, r, deps)) { + return true; + } + if (!ctx.inconsistent() && (_l != l || _r != r)) { + m_eqs.push_back(eq(l, r, deps)); + return true; + } + return false; +} + +bool theory_seq::is_binary_eq(expr* l, expr* r, expr*& x, ptr_vector& xs, ptr_vector& ys, expr*& y) { + xs.reset(); + ys.reset(); + get_concat(l, xs); + if (xs.size() > 1 && is_var(xs[0])) { + get_concat(r, ys); + if (ys.size() > 1 && is_var(ys.back())) { + x = xs[0]; + y = ys.back(); + for (unsigned i = 1; i < xs.size(); ++i) { + if (!m_util.str.is_unit(xs[i])) return false; + xs[i-1] = xs[i]; + } + xs.pop_back(); + for (unsigned i = 0; i < ys.size()-1; ++i) { + if (!m_util.str.is_unit(ys[i])) return false; + } + ys.pop_back(); + return true; + } + } + return false; +} + +bool theory_seq::solve_binary_eq(expr* l, expr* r, dependency* dep) { + context& ctx = get_context(); + ptr_vector xs, ys; + expr* x, *y; + bool is_binary = is_binary_eq(l, r, x, xs, ys, y); + if (!is_binary) { + std::swap(l, r); + is_binary = is_binary_eq(l, r, x, xs, ys, y); + } + if (!is_binary) { + return false; + } + // Equation is of the form x ++ xs = ys ++ y + // where xs, ys are units. + if (x != y) { + return false; + } + if (xs.size() != ys.size()) { + set_conflict(dep); + return false; + } + if (xs.empty()) { + // this should have been solved already + UNREACHABLE(); + return false; + } + unsigned sz = xs.size(); + literal_vector conflict; + for (unsigned offset = 0; offset < sz; ++offset) { + bool has_conflict = false; + for (unsigned j = 0; !has_conflict && j < sz; ++j) { + unsigned j1 = (offset + j) % sz; + literal eq = mk_eq(xs[j], ys[j1], false); + switch (ctx.get_assignment(eq)) { + case l_false: + conflict.push_back(~eq); + has_conflict = true; + break; + case l_undef: { + enode* n1 = ensure_enode(xs[j]); + enode* n2 = ensure_enode(ys[j1]); + if (n1->get_root() == n2->get_root()) { + break; + } + ctx.mark_as_relevant(eq); + if (sz == 1) { + propagate_lit(dep, 0, 0, eq); + return true; + } + m_new_propagation = true; + break; + } + case l_true: + break; + } + } + if (!has_conflict) { + TRACE("seq", tout << "offset: " << offset << " equality "; + for (unsigned j = 0; j < sz; ++j) { + tout << mk_pp(xs[j], m) << " = " << mk_pp(ys[(offset+j) % sz], m) << "; "; + } + tout << "\n";); + // current equalities can work when solving x ++ xs = ys ++ y + return false; + } + } + TRACE("seq", tout << conflict << "\n";); + set_conflict(dep, conflict); + return false; } bool theory_seq::solve_nqs() { @@ -740,10 +846,7 @@ bool theory_seq::solve_ne(unsigned idx) { mark_solved(idx); return change; } - else if (unchanged(l, lhs) && unchanged(r, rhs)) { - // continue - } - else if (unchanged(r, lhs) && unchanged(l, rhs)) { + else if (unchanged(l, lhs, r, rhs) ) { // continue } else { @@ -807,12 +910,13 @@ void theory_seq::erase_index(unsigned idx, unsigned i) { bool theory_seq::simplify_and_solve_eqs() { context & ctx = get_context(); - bool propagated = false; - simplify_eqs(propagated); - while (!ctx.inconsistent() && solve_basic_eqs(propagated)) { - simplify_eqs(propagated); + m_new_propagation = false; + m_new_solution = true; + while (m_new_solution && !ctx.inconsistent()) { + m_new_solution = false; + solve_eqs(0); } - return propagated || ctx.inconsistent(); + return m_new_propagation || ctx.inconsistent(); } @@ -1121,7 +1225,7 @@ theory_var theory_seq::mk_var(enode* n) { } bool theory_seq::can_propagate() { - return m_axioms_head < m_axioms.size() || !m_replay.empty(); + return m_axioms_head < m_axioms.size() || !m_replay.empty() || m_new_solution; } expr_ref theory_seq::canonize(expr* e, dependency*& eqs) { @@ -1190,6 +1294,10 @@ void theory_seq::propagate() { TRACE("seq", tout << "replay: " << ctx.get_scope_level() << "\n";); m_replay.pop_back(); } + if (m_new_solution) { + simplify_and_solve_eqs(); + m_new_solution = false; + } } void theory_seq::enque_axiom(expr* e) { @@ -1199,7 +1307,6 @@ void theory_seq::enque_axiom(expr* e) { m_axiom_set.insert(e); m_trail_stack.push(push_back_vector(m_axioms)); m_trail_stack.push(insert_obj_trail(m_axiom_set, e));; - } } @@ -1353,6 +1460,7 @@ void theory_seq::add_elim_string_axiom(expr* n) { } add_axiom(mk_eq(n, result, false)); m_rep.update(n, result, 0); + m_new_solution = true; } @@ -1607,11 +1715,12 @@ void theory_seq::add_at_axiom(expr* e) { */ void theory_seq::propagate_step(literal lit, expr* step) { context& ctx = get_context(); + SASSERT(ctx.get_assignment(lit) == l_true); expr* re, *t, *s, *idx, *i, *j; VERIFY(is_step(step, s, idx, re, i, j, t)); expr_ref nth = mk_nth(s, idx); + TRACE("seq", tout << mk_pp(step, m) << " -> " << mk_pp(t, m) << " = " << nth << "\n";); propagate_eq(lit, t, nth); - SASSERT(ctx.get_assignment(lit) == l_true); propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); ensure_nth(lit, s, idx); } @@ -1683,6 +1792,7 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4) { if (l3 != null_literal) { ctx.mark_as_relevant(l3); lits.push_back(l3); } if (l4 != null_literal) { ctx.mark_as_relevant(l4); lits.push_back(l4); } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + m_new_propagation = true; ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } @@ -1726,6 +1836,7 @@ void theory_seq::propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs) ext_theory_eq_propagation_justification( get_id(), ctx.get_region(), 1, &lit, 0, 0, n1, n2)); + m_new_propagation = true; ctx.assign_eq(n1, n2, eq_justification(js)); } @@ -1834,10 +1945,8 @@ void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { expr_ref o1(n1->get_owner(), m); expr_ref o2(n2->get_owner(), m); TRACE("seq", tout << o1 << " = " << o2 << "\n";); - bool propagated = false; - if (!simplify_eq(o1, o2, deps, propagated)) { - m_eqs.push_back(eq(o1, o2, deps)); - } + m_eqs.push_back(eq(o1, o2, deps)); + solve_eqs(m_eqs.size()-1); enforce_length_coherence(n1, n2); } } @@ -1865,6 +1974,7 @@ void theory_seq::push_scope_eh() { m_trail_stack.push(value_trail(m_axioms_head)); m_eqs.push_scope(); m_nqs.push_scope(); + m_atoms_lim.push_back(m_atoms.size()); } void theory_seq::pop_scope_eh(unsigned num_scopes) { @@ -1875,6 +1985,8 @@ void theory_seq::pop_scope_eh(unsigned num_scopes) { m_exclude.pop_scope(num_scopes); m_eqs.pop_scope(num_scopes); m_nqs.pop_scope(num_scopes); + m_atoms.resize(m_atoms_lim[m_atoms_lim.size()-num_scopes]); + m_atoms_lim.shrink(m_atoms_lim.size()-num_scopes); } void theory_seq::restart_eh() { @@ -2007,6 +2119,8 @@ void theory_seq::propagate_acc_rej_length(literal lit, expr* e) { */ bool theory_seq::add_accept2step(expr* acc) { context& ctx = get_context(); + + TRACE("seq", tout << mk_pp(acc, m) << "\n";); SASSERT(ctx.get_assignment(acc) == l_true); expr *e, * idx, *re; expr_ref step(m); @@ -2025,24 +2139,44 @@ bool theory_seq::add_accept2step(expr* acc) { lits.push_back(~ctx.get_literal(acc)); if (aut->is_final_state(src)) { lits.push_back(mk_literal(m_autil.mk_le(len, idx))); - if (ctx.get_assignment(lits.back()) == l_true) { + switch (ctx.get_assignment(lits.back())) { + case l_true: return false; + case l_undef: + ctx.force_phase(lits.back()); + return true; + default: + break; } } + bool has_undef = false; + int start = ctx.get_random_value(); for (unsigned i = 0; i < mvs.size(); ++i) { - eautomaton::move mv = mvs[i]; + unsigned j = (i + start) % mvs.size(); + eautomaton::move mv = mvs[j]; step = mk_step(e, idx, re, src, mv.dst(), mv.t()); lits.push_back(mk_literal(step)); - if (ctx.get_assignment(lits.back()) == l_true) { + switch (ctx.get_assignment(lits.back())) { + case l_true: return false; + case l_undef: + //ctx.force_phase(lits.back()); + //return true; + has_undef = true; + break; + default: + break; } } + if (has_undef) { + return true; + } TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); - //std::cout << lits << "\n"; - for (unsigned i = 0; i < lits.size(); ++i) { // TBD - ctx.mark_as_relevant(lits[i]); + for (unsigned i = 0; i < lits.size(); ++i) { + SASSERT(ctx.get_assignment(lits[i]) == l_false); + lits[i].neg(); } - ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + set_conflict(0, lits); return false; } @@ -2290,7 +2424,7 @@ bool theory_seq::add_contains2contains(expr* e) { expr_ref head(m), tail(m); mk_decompose(e1, head, tail); literal lits[2] = { ~ctx.get_literal(e), ~mk_eq(e1, emp, false) }; - propagate_lit(0, 2, lits, ~mk_literal(m_util.str.mk_contains(tail, e1))); + propagate_lit(0, 2, lits, ~mk_literal(m_util.str.mk_contains(tail, e2))); return false; } @@ -2306,7 +2440,7 @@ bool theory_seq::propagate_automata() { TRACE("seq", tout << mk_pp(e, m) << "\n";); bool reQ = false; if (is_accept(e)) { - add_accept2step(e); + reQ = add_accept2step(e); } else if (is_reject(e)) { reQ = add_reject2reject(e); @@ -2325,12 +2459,8 @@ bool theory_seq::propagate_automata() { } if (reQ) { re_add.push_back(e); - m_atoms[m_atoms_qhead] = m_atoms.back(); - m_atoms.pop_back(); - } - else { - ++m_atoms_qhead; } + ++m_atoms_qhead; } m_atoms.append(re_add); return true; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 341c1eb07..78eebe301 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -73,7 +73,7 @@ namespace smt { public: solution_map(ast_manager& m, dependency_manager& dm): m(m), m_dm(dm), m_cache(m), m_lhs(m), m_rhs(m) {} - bool empty() const { return m_map.empty(); } + bool empty() const { return m_map.empty(); } void update(expr* e, expr* r, dependency* d); void add_cache(expr* v, expr_dep& r) { m_cache.insert(v, r); } bool find_cache(expr* v, expr_dep& r) { return m_cache.find(v, r); } @@ -81,10 +81,10 @@ namespace smt { expr* find(expr* e); bool is_root(expr* e) const; void cache(expr* e, expr* r, dependency* d); - void reset_cache() { m_cache.reset(); } - void push_scope() { m_limit.push_back(m_updates.size()); } - void pop_scope(unsigned num_scopes); - void display(std::ostream& out) const; + void reset_cache() { m_cache.reset(); } + void push_scope() { m_limit.push_back(m_updates.size()); } + void pop_scope(unsigned num_scopes); + void display(std::ostream& out) const; }; // Table of current disequalities @@ -109,7 +109,7 @@ namespace smt { struct eq { expr_ref m_lhs; expr_ref m_rhs; - dependency* m_dep; + dependency* m_dep; eq(expr_ref& l, expr_ref& r, dependency* d): m_lhs(l), m_rhs(r), m_dep(d) {} eq(eq const& other): m_lhs(other.m_lhs), m_rhs(other.m_rhs), m_dep(other.m_dep) {} @@ -283,37 +283,42 @@ namespace smt { unsigned m_solve_nqs; unsigned m_solve_eqs; }; - ast_manager& m; - dependency_manager m_dm; - solution_map m_rep; // unification representative. - scoped_vector m_eqs; // set of current equations. - scoped_vector m_nqs; // set of current disequalities. + ast_manager& m; + dependency_manager m_dm; + solution_map m_rep; // unification representative. + scoped_vector m_eqs; // set of current equations. + scoped_vector m_nqs; // set of current disequalities. - seq_factory* m_factory; // value factory - exclusion_table m_exclude; // set of asserted disequalities. - expr_ref_vector m_axioms; // list of axioms to add. - obj_hashtable m_axiom_set; - unsigned m_axioms_head; // index of first axiom to add. - unsigned m_branch_variable_head; // index of first equation to examine. - bool m_incomplete; // is the solver (clearly) incomplete for the fragment. - obj_hashtable m_length; // is length applied - scoped_ptr_vector m_replay; // set of actions to replay + seq_factory* m_factory; // value factory + exclusion_table m_exclude; // set of asserted disequalities. + expr_ref_vector m_axioms; // list of axioms to add. + obj_hashtable m_axiom_set; + unsigned m_axioms_head; // index of first axiom to add. + unsigned m_branch_variable_head; // index of first equation to examine. + bool m_incomplete; // is the solver (clearly) incomplete for the fragment. + obj_hashtable m_length; // is length applied + scoped_ptr_vector m_replay; // set of actions to replay model_generator* m_mg; - th_rewriter m_rewrite; - seq_util m_util; - arith_util m_autil; - th_trail_stack m_trail_stack; - stats m_stats; - symbol m_prefix, m_suffix, m_contains_left, m_contains_right, m_accept, m_reject; - symbol m_tail, m_nth, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; - symbol m_extract_prefix, m_at_left, m_at_right; + th_rewriter m_rewrite; + seq_util m_util; + arith_util m_autil; + th_trail_stack m_trail_stack; + stats m_stats; + symbol m_prefix, m_suffix, m_contains_left, m_contains_right, m_accept, m_reject; + symbol m_tail, m_nth, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; + symbol m_extract_prefix, m_at_left, m_at_right; ptr_vector m_todo; // maintain automata with regular expressions. scoped_ptr_vector m_automata; obj_map m_re2aut; + + // queue of asserted atoms ptr_vector m_atoms; + unsigned_vector m_atoms_lim; unsigned m_atoms_qhead; + bool m_new_solution; // new solution added + bool m_new_propagation; // new propagation to core virtual final_check_status final_check_eh(); virtual bool internalize_atom(app* atom, bool) { return internalize_term(atom); } @@ -345,15 +350,21 @@ namespace smt { bool check_length_coherence(); bool propagate_length_coherence(expr* e); - bool pre_process_eqs(bool simplify_or_solve, bool& propagated); - bool simplify_eqs(bool& propagated) { return pre_process_eqs(true, propagated); } - bool solve_basic_eqs(bool& propagated) { return pre_process_eqs(false, propagated); } - bool simplify_eq(expr* l, expr* r, dependency* dep, bool& propagated); - bool solve_unit_eq(expr* l, expr* r, dependency* dep, bool& propagated); + bool solve_eqs(unsigned start); + bool solve_eq(expr* l, expr* r, dependency* dep); + bool simplify_eq(expr* l, expr* r, dependency* dep); + bool solve_unit_eq(expr* l, expr* r, dependency* dep); + bool is_binary_eq(expr* l, expr* r, expr*& x, ptr_vector& xunits, ptr_vector& yunits, expr*& y); + bool solve_binary_eq(expr* l, expr* r, dependency* dep); bool solve_nqs(); bool solve_ne(unsigned i); bool unchanged(expr* e, expr_ref_vector& es) const { return es.size() == 1 && es[0] == e; } + bool unchanged(expr* e, expr_ref_vector& es, expr* f, expr_ref_vector& fs) const { + return + (unchanged(e, es) && unchanged(f, fs)) || + (unchanged(e, fs) && unchanged(e, fs)); + } // asserting consequences void linearize(dependency* dep, enode_pair_vector& eqs, literal_vector& lits) const; @@ -363,7 +374,7 @@ namespace smt { void propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs = false); void set_conflict(dependency* dep, literal_vector const& lits = literal_vector()); - bool find_branch_candidate(expr* l, expr_ref_vector const& rs); + bool find_branch_candidate(dependency* dep, expr* l, expr_ref_vector const& rs); lbool assume_equality(expr* l, expr* r); // variable solving utilities From e10ecad5dc0158049f3b893d43773389170c4610 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Jan 2016 22:52:28 -0800 Subject: [PATCH 24/35] seq API Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 32 ++++ src/api/api_context.cpp | 2 + src/api/api_context.h | 5 + src/api/api_seq.cpp | 151 ++++++++++++++++++ src/api/api_util.h | 17 +++ src/api/python/z3.py | 252 ++++++++++++++++++++++++++++++- src/api/python/z3printer.py | 5 + src/api/z3_api.h | 233 +++++++++++++++++++++++++++- src/ast/seq_decl_plugin.cpp | 5 +- src/ast/seq_decl_plugin.h | 9 +- src/math/automata/automaton.h | 23 ++- src/parsers/smt2/smt2scanner.cpp | 16 +- src/smt/theory_seq.cpp | 35 +++-- src/smt/theory_seq.h | 4 +- 14 files changed, 745 insertions(+), 44 deletions(-) create mode 100644 src/api/api_seq.cpp diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 944e447c6..db10c8bb3 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -641,6 +641,12 @@ extern "C" { else if (fid == mk_c(c)->get_fpa_fid() && k == ROUNDING_MODE_SORT) { return Z3_ROUNDING_MODE_SORT; } + else if (fid == mk_c(c)->get_seq_fid() && k == SEQ_SORT) { + return Z3_SEQ_SORT; + } + else if (fid == mk_c(c)->get_seq_fid() && k == RE_SORT) { + return Z3_RE_SORT; + } else { return Z3_UNKNOWN_SORT; } @@ -1108,6 +1114,32 @@ extern "C" { } } + if (mk_c(c)->get_seq_fid() == _d->get_family_id()) { + switch (_d->get_decl_kind()) { + case Z3_OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT; + case Z3_OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY; + case Z3_OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT; + case Z3_OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX; + case Z3_OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX; + case Z3_OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS; + case Z3_OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT; + case Z3_OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE; + case Z3_OP_SEQ_AT: return Z3_OP_SEQ_AT; + case Z3_OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH; + case Z3_OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX; + case Z3_OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE; + case Z3_OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE; + + case Z3_OP_RE_PLUS: return Z3_OP_RE_PLUS; + case Z3_OP_RE_STAR: return Z3_OP_RE_STAR; + case Z3_OP_RE_OPTION: return Z3_OP_RE_OPTION; + case Z3_OP_RE_CONCAT: return Z3_OP_RE_CONCAT; + case Z3_OP_RE_UNION: return Z3_OP_RE_UNION; + default: + return Z3_OP_UNINTERPRETED; + } + } + if (mk_c(c)->get_fpa_fid() == _d->get_family_id()) { switch (_d->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN; diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 9d0abe3a7..75782a28d 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -73,6 +73,7 @@ namespace api { m_datalog_util(m()), m_fpa_util(m()), m_dtutil(m()), + m_sutil(m()), m_last_result(m()), m_ast_trail(m()), m_replay_stack(), @@ -97,6 +98,7 @@ namespace api { m_dt_fid = m().mk_family_id("datatype"); m_datalog_fid = m().mk_family_id("datalog_relation"); m_fpa_fid = m().mk_family_id("fpa"); + m_seq_fid = m().mk_family_id("seq"); m_dt_plugin = static_cast(m().get_plugin(m_dt_fid)); if (!m_user_ref_count) { diff --git a/src/api/api_context.h b/src/api/api_context.h index 40b59d1b2..23c8d3fd2 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -25,6 +25,7 @@ Revision History: #include"api_util.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" +#include"seq_decl_plugin.h" #include"datatype_decl_plugin.h" #include"dl_decl_plugin.h" #include"fpa_decl_plugin.h" @@ -58,6 +59,7 @@ namespace api { datalog::dl_decl_util m_datalog_util; fpa_util m_fpa_util; datatype_util m_dtutil; + seq_util m_sutil; // Support for old solver API smt_params m_fparams; @@ -78,6 +80,7 @@ namespace api { family_id m_datalog_fid; family_id m_pb_fid; family_id m_fpa_fid; + family_id m_seq_fid; datatype_decl_plugin * m_dt_plugin; std::string m_string_buffer; // temporary buffer used to cache strings sent to the "external" world. @@ -121,6 +124,7 @@ namespace api { datalog::dl_decl_util & datalog_util() { return m_datalog_util; } fpa_util & fpautil() { return m_fpa_util; } datatype_util& dtutil() { return m_dtutil; } + seq_util& sutil() { return m_sutil; } family_id get_basic_fid() const { return m_basic_fid; } family_id get_array_fid() const { return m_array_fid; } family_id get_arith_fid() const { return m_arith_fid; } @@ -129,6 +133,7 @@ namespace api { family_id get_datalog_fid() const { return m_datalog_fid; } family_id get_pb_fid() const { return m_pb_fid; } family_id get_fpa_fid() const { return m_fpa_fid; } + family_id get_seq_fid() const { return m_seq_fid; } datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; } Z3_error_code get_error_code() const { return m_error_code; } diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp new file mode 100644 index 000000000..0949cc2e4 --- /dev/null +++ b/src/api/api_seq.cpp @@ -0,0 +1,151 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + api_seq.cpp + +Abstract: + + API for sequences and regular expressions. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-01-02. + +Revision History: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_context.h" +#include"api_util.h" +#include"ast_pp.h" + +extern "C" { + + Z3_sort Z3_API Z3_mk_seq_sort(Z3_context c, Z3_sort domain) { + Z3_TRY; + LOG_Z3_mk_seq_sort(c, domain); + RESET_ERROR_CODE(); + sort * ty = mk_c(c)->sutil().str.mk_seq(to_sort(domain)); + mk_c(c)->save_ast_trail(ty); + RETURN_Z3(of_sort(ty)); + Z3_CATCH_RETURN(0); + } + + Z3_sort Z3_API Z3_mk_re_sort(Z3_context c, Z3_sort domain) { + Z3_TRY; + LOG_Z3_mk_re_sort(c, domain); + RESET_ERROR_CODE(); + sort * ty = mk_c(c)->sutil().re.mk_re(to_sort(domain)); + mk_c(c)->save_ast_trail(ty); + RETURN_Z3(of_sort(ty)); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string str) { + Z3_TRY; + LOG_Z3_mk_string(c, str); + RESET_ERROR_CODE(); + zstring s(str, zstring::ascii); + 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(0); + } + + Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) { + Z3_TRY; + LOG_Z3_mk_string_sort(c); + RESET_ERROR_CODE(); + sort* ty = mk_c(c)->sutil().str.mk_string_sort(); + mk_c(c)->save_ast_trail(ty); + RETURN_Z3(of_sort(ty)); + Z3_CATCH_RETURN(0); + } + + Z3_bool Z3_API Z3_is_seq_sort(Z3_context c, Z3_sort s) { + Z3_TRY; + LOG_Z3_is_seq_sort(c, s); + RESET_ERROR_CODE(); + bool result = mk_c(c)->sutil().is_seq(to_sort(s)); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_is_re_sort(Z3_context c, Z3_sort s) { + Z3_TRY; + LOG_Z3_is_re_sort(c, s); + RESET_ERROR_CODE(); + bool result = mk_c(c)->sutil().is_re(to_sort(s)); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_is_string_sort(Z3_context c, Z3_sort s) { + Z3_TRY; + LOG_Z3_is_string_sort(c, s); + RESET_ERROR_CODE(); + bool result = mk_c(c)->sutil().is_string(to_sort(s)); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_is_string(Z3_context c, Z3_ast s) { + Z3_TRY; + LOG_Z3_is_string(c, s); + RESET_ERROR_CODE(); + bool result = mk_c(c)->sutil().str.is_string(to_expr(s)); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s) { + Z3_TRY; + LOG_Z3_get_string(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); + return ""; + } + std::string result = str.encode(); + return mk_c(c)->mk_external_string(result); + Z3_CATCH_RETURN(""); + } + + Z3_ast Z3_API Z3_mk_seq_empty(Z3_context c, Z3_sort seq) { + Z3_TRY; + LOG_Z3_mk_seq_empty(c, seq); + RESET_ERROR_CODE(); + app* a = mk_c(c)->sutil().str.mk_empty(to_sort(seq)); + mk_c(c)->save_ast_trail(a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } + + MK_UNARY(Z3_mk_seq_unit, mk_c(c)->get_seq_fid(), OP_SEQ_UNIT, SKIP); + MK_NARY(Z3_mk_seq_concat, mk_c(c)->get_seq_fid(), OP_SEQ_CONCAT, SKIP); + MK_BINARY(Z3_mk_seq_prefix, mk_c(c)->get_seq_fid(), OP_SEQ_PREFIX, SKIP); + MK_BINARY(Z3_mk_seq_suffix, mk_c(c)->get_seq_fid(), OP_SEQ_SUFFIX, SKIP); + MK_BINARY(Z3_mk_seq_contains, mk_c(c)->get_seq_fid(), OP_SEQ_CONTAINS, SKIP); + MK_TERNARY(Z3_mk_seq_extract, mk_c(c)->get_seq_fid(), OP_SEQ_EXTRACT, SKIP); + MK_TERNARY(Z3_mk_seq_replace, mk_c(c)->get_seq_fid(), OP_SEQ_REPLACE, SKIP); + MK_BINARY(Z3_mk_seq_at, mk_c(c)->get_seq_fid(), OP_SEQ_AT, SKIP); + MK_UNARY(Z3_mk_seq_length, mk_c(c)->get_seq_fid(), OP_SEQ_LENGTH, SKIP); + //MK_BINARY(Z3_mk_seq_index, mk_c(c)->get_seq_fid(), OP_SEQ_INDEX, SKIP); + MK_UNARY(Z3_mk_seq_to_re, mk_c(c)->get_seq_fid(), OP_SEQ_TO_RE, SKIP); + MK_BINARY(Z3_mk_seq_in_re, mk_c(c)->get_seq_fid(), OP_SEQ_IN_RE, SKIP); + + + MK_UNARY(Z3_mk_re_plus, mk_c(c)->get_seq_fid(), OP_RE_PLUS, SKIP); + MK_UNARY(Z3_mk_re_star, mk_c(c)->get_seq_fid(), OP_RE_STAR, SKIP); + MK_UNARY(Z3_mk_re_option, mk_c(c)->get_seq_fid(), OP_RE_OPTION, SKIP); + MK_NARY(Z3_mk_re_union, mk_c(c)->get_seq_fid(), OP_RE_UNION, SKIP); + MK_NARY(Z3_mk_re_concat, mk_c(c)->get_seq_fid(), OP_RE_CONCAT, SKIP); + + + +}; diff --git a/src/api/api_util.h b/src/api/api_util.h index 9befd8849..3b7baeac0 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -135,6 +135,23 @@ Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2) { \ MK_BINARY_BODY(NAME, FID, OP, EXTRA_CODE); \ } +#define MK_TERNARY_BODY(NAME, FID, OP, EXTRA_CODE) \ + Z3_TRY; \ + RESET_ERROR_CODE(); \ + EXTRA_CODE; \ + expr * args[3] = { to_expr(n1), to_expr(n2), to_expr(n3) }; \ + ast* a = mk_c(c)->m().mk_app(FID, OP, 0, 0, 3, args); \ + mk_c(c)->save_ast_trail(a); \ + check_sorts(c, a); \ + RETURN_Z3(of_ast(a)); \ + Z3_CATCH_RETURN(0); + +#define MK_TERNARY(NAME, FID, OP, EXTRA_CODE) \ + Z3_ast Z3_API NAME(Z3_context c, Z3_ast n1, Z3_ast n2, Z3_ast n3) { \ + LOG_ ## NAME(c, n1, n2, n3); \ + MK_TERNARY_BODY(NAME, FID, OP, EXTRA_CODE); \ +} + #define MK_NARY(NAME, FID, OP, EXTRA_CODE) \ Z3_ast Z3_API NAME(Z3_context c, unsigned num_args, Z3_ast const* args) { \ Z3_TRY; \ diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 864a92846..507e9bfee 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -930,6 +930,10 @@ def _to_expr_ref(a, ctx): return FiniteDomainRef(a, ctx) if sk == Z3_ROUNDING_MODE_SORT: return FPRMRef(a, ctx) + if sk == Z3_SEQ_SORT: + return SeqRef(a, ctx) + if sk == Z3_RE_SORT: + return ReRef(a, ctx) return ExprRef(a, ctx) def _coerce_expr_merge(s, a): @@ -3562,12 +3566,32 @@ def Concat(*args): 121 """ args = _get_args(args) + sz = len(args) + if __debug__: + _z3_assert(sz >= 2, "At least two arguments expected.") + + ctx = args[0].ctx + + if is_seq(args[0]): + if __debug__: + _z3_assert(all([is_seq(a) for a in args]), "All arguments must be sequence expressions.") + v = (Ast * sz)() + for i in range(sz): + v[i] = args[i].as_ast() + return SeqRef(Z3_mk_seq_concat(ctx.ref(), sz, v), ctx) + + if is_re(args[0]): + if __debug__: + _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") + v = (Ast * sz)() + for i in range(sz): + v[i] = args[i].as_ast() + return ReRef(Z3_mk_re_concat(ctx.ref(), sz, v), ctx) + if __debug__: _z3_assert(all([is_bv(a) for a in args]), "All arguments must be Z3 bit-vector expressions.") - _z3_assert(len(args) >= 2, "At least two arguments expected.") - ctx = args[0].ctx - r = args[0] - for i in range(len(args) - 1): + r = args[0] + for i in range(sz - 1): r = BitVecRef(Z3_mk_concat(ctx.ref(), r.as_ast(), args[i+1].as_ast()), ctx) return r @@ -7781,7 +7805,7 @@ def binary_interpolant(a,b,p=None,ctx=None): solver that determines satisfiability. x = Int('x') - print binary_interpolant(x<0,x>2) + print(binary_interpolant(x<0,x>2)) Not(x >= 0) """ f = And(Interpolant(a),b) @@ -7821,6 +7845,7 @@ def sequence_interpolant(v,p=None,ctx=None): f = And(Interpolant(f),v[i]) return tree_interpolant(f,p,ctx) + ######################################### # # Floating-Point Arithmetic @@ -8887,3 +8912,220 @@ def fpToIEEEBV(x): if __debug__: _z3_assert(is_fp(x), "First argument must be a Z3 floating-point expression") return BitVecRef(Z3_mk_fpa_to_ieee_bv(x.ctx_ref(), x.ast), x.ctx) + + + +######################################### +# +# Strings, Sequences and Regular expressions +# +######################################### + +class SeqSortRef(SortRef): + """Sequence sort.""" + + def is_string(self): + """Determine if sort is a string + >>> s = StringSort() + >>> s.is_string() + True + >>> s = SeqSort(IntSort()) + >>> s.is_string() + False + """ + return Z3_is_string_sort(self.ctx_ref(), self.ast) + +def StringSort(ctx=None): + """Create a string sort + >>> s = StringSort() + >>> print(s) + String + """ + ctx = _get_ctx(ctx) + return SeqSortRef(Z3_mk_string_sort(ctx.ref()), ctx) + + +def SeqSort(s): + """Create a sequence sort over elements provided in the argument + >>> s = SeqSort(IntSort()) + >>> s == Unit(IntVal(1)).sort() + True + """ + return SeqSortRef(Z3_mk_seq_sort(s.ctx_ref(), s.ast), s.ctx) + +class SeqRef(ExprRef): + """Sequence expression.""" + + def sort(self): + return SeqSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) + + def __add__(self, other): + v = (Ast * 2)() + v[0] = self.as_ast() + v[1] = other.as_ast() + return SeqRef(Z3_mk_seq_concat(self.ctx_ref(), 2, v), self.ctx) + + def __getitem__(self, i): + return SeqRef(Z3_mk_seq_at(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) + + def is_string_sort(self): + return Z3_is_string_sort(self.ctx_ref(), Z3_get_sort(self.ctx_ref(), self.as_ast())) + + def is_string_value(self): + return Z3_is_string(self.ctx_ref(), self.as_ast()) + + def as_string(self): + """Return a string representation of sequence expression.""" + return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) + + +def _coerce_seq(s, ctx=None): + if isinstance(s, str): + ctx = _get_ctx(ctx) + s = String(s, ctx) + return s + +def _get_ctx2(a, b): + if is_expr(a): + return a.ctx + if is_expr(b): + return b.ctx + return None + +def is_seq(a): + """Return `True` if `a` is a Z3 sequence expression.""" + return isinstance(a, SeqRef) + +def is_string_sort(a): + """Return `True` if `a` is a Z3 string expression.""" + return isinstance(a, SeqRef) and a.is_string_sort() + +def is_string_value(a): + """return 'True' if 'a' is a Z3 string constant expression.""" + return isinstance(a, SeqRef) and a.is_string_value() + +def String(s, ctx=None): + """create a string expression""" + ctx = _get_ctx(ctx) + return SeqRef(Z3_mk_string(ctx.ref(), s), ctx) + +def Empty(s): + """Create the empty sequence of the given sort + >>> e = Empty(StringSort()) + >>> print(e) + "" + >>> e2 = String("") + >>> print(e == e2) + True + >>> e3 = Empty(SeqSort(IntSort())) + >>> print(e3) + """ + return SeqRef(Z3_mk_seq_empty(s.ctx_ref(), s.as_ast()), s.ctx) + +def Unit(a): + """Create a singleton sequence""" + return SeqRef(Z3_mk_seq_unit(a.ctx_ref(), a.as_ast()), a.ctx) + +def PrefixOf(a, b): + """Check if 'a' is a prefix of 'b'""" + ctx = _get_ctx2(a, b) + a = _coerce_seq(a, ctx) + b = _coerce_seq(b, ctx) + return BoolRef(Z3_mk_seq_prefix(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) + +def SuffixOf(a, b): + """Check if 'a' is a suffix of 'b'""" + ctx = _get_ctx2(a, b) + a = _coerce_seq(a, ctx) + b = _coerce_seq(b, ctx) + return BoolRef(Z3_mk_seq_suffix(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) + +def Contains(a, b): + """Check if 'a' contains 'b'""" + ctx = _get_ctx2(a, b) + a = _coerce_seq(a, ctx) + b = _coerce_seq(b, ctx) + return BoolRef(Z3_mk_seq_contains(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) + +#def Extract(a, offset, length): +# """Extract a sequence at offset with indicated length""" +# return SeqRef(Z3_mk_seq_extract(a.ctx_ref(), a.as_ast(), offset.as_ast()), length.ctx) + +def Replace(src, dst, s): + """Replace the first occurrence of 'src' by 'dst' in 's'""" + ctx = _get_ctx2(src, _get_ctx2(dst, s)) + src = _coerce_seq(src, ctx) + dst = _coerce_seq(dst, ctx) + s = _coerce_seq(s, ctx) + return SeqRef(Z3_mk_seq_replace(src.ctx_ref(), src.as_ast(), dst.as_ast()), s.ctx) + +def Length(s): + """Obtain the length of a sequence 's'""" + return ArithRef(Z3_mk_seq_length(s.ctx_ref(), s.as_ast(), s.ctx)) + +def Re(s, ctx=None): + """The regular expression that accepts sequence 's'""" + s = _coerce_seq(s, ctx) + return ReRef(Z3_mk_seq_to_re(s.ctx_ref(), s.as_ast()), s.ctx) + + + + +## Regular expressions + +class ReSortRef(SortRef): + """Regular expression sort.""" + + +def ReSort(s): + if is_ast(s): + return ReSortRef(Z3_mk_re_sort(s.ctx.ref(), s.as_ast()), ctx) + if s is None or isinstance(s, Context): + ctx = _get_ctx(s) + return ReSortRef(Z3_mk_re_sort(ctx.ref(), Z3_mk_string_sort(ctx.ref())), ctx) + raise Z3Exception("Regular expression sort constructor expects either a string or a context or no argument") + + +class ReRef(ExprRef): + """Regular expressions.""" + + def __add__(self, other): + v = (Ast * 2)() + v[0] = self.as_ast() + v[1] = other.as_ast() + return SeqRef(Z3_mk_re_union(self.ctx_ref(), 2, v), self.ctx) + + +def is_re(s): + return isinstance(s, ReRef) + +def InRe(s, re): + s = _coerce_seq(s, re.ctx) + return BoolRef(Z3_mk_seq_in_re(s.ctx_ref(), s.as_ast(), re.as_ast()), s.ctx) + +def Plus(re): + """Create the regular expression accepting one or more repetitions of argument. + >>> re = Plus(Re("a")) + >>> print(simplify(InRe("aa", re))) + True + >>> print(simplify(InRe("ab", re))) + False + >>> print(simplify(InRe("", re))) + False + """ + return ReRef(Z3_mk_re_plus(re.ctx_ref(), re.as_ast()), re.ctx) + +def Option(re): + return ReRef(Z3_mk_re_option(re.ctx_ref(), re.as_ast()), re.ctx) + +def Star(re): + """Create the regular expression accepting zero or more repetitions of argument. + >>> re = Star(Re("a")) + >>> print(simplify(InRe("aa", re))) + True + >>> print(simplify(InRe("ab", re))) + False + >>> print(simplify(InRe("", re))) + True + """ + return ReRef(Z3_mk_re_star(re.ctx_ref(), re.as_ast()), re.ctx) diff --git a/src/api/python/z3printer.py b/src/api/python/z3printer.py index adc203436..24d0359c9 100644 --- a/src/api/python/z3printer.py +++ b/src/api/python/z3printer.py @@ -570,6 +570,9 @@ class Formatter: def pp_algebraic(self, a): return to_format(a.as_decimal(self.precision)) + def pp_string(self, a): + return to_format(a.as_string()) + def pp_bv(self, a): return to_format(a.as_string()) @@ -875,6 +878,8 @@ class Formatter: return self.pp_fp_value(a) elif z3.is_fp(a): return self.pp_fp(a, d, xs) + elif z3.is_string_value(a): + return self.pp_string(a) elif z3.is_const(a): return self.pp_const(a) else: diff --git a/src/api/z3_api.h b/src/api/z3_api.h index a4a10bb85..e00134c06 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -161,6 +161,8 @@ typedef enum Z3_FINITE_DOMAIN_SORT, Z3_FLOATING_POINT_SORT, Z3_ROUNDING_MODE_SORT, + Z3_SEQ_SORT, + Z3_RE_SORT, Z3_UNKNOWN_SORT = 1000 } Z3_sort_kind; @@ -1098,7 +1100,7 @@ typedef enum { Z3_OP_PR_TH_LEMMA, Z3_OP_PR_HYPER_RESOLVE, - // Sequences + // Relational algebra Z3_OP_RA_STORE = 0x600, Z3_OP_RA_EMPTY, Z3_OP_RA_IS_EMPTY, @@ -1115,6 +1117,28 @@ typedef enum { Z3_OP_FD_CONSTANT, Z3_OP_FD_LT, + // Sequences + Z3_OP_SEQ_UNIT, + Z3_OP_SEQ_EMPTY, + Z3_OP_SEQ_CONCAT, + Z3_OP_SEQ_PREFIX, + Z3_OP_SEQ_SUFFIX, + Z3_OP_SEQ_CONTAINS, + Z3_OP_SEQ_EXTRACT, + Z3_OP_SEQ_REPLACE, + Z3_OP_SEQ_AT, + Z3_OP_SEQ_LENGTH, + Z3_OP_SEQ_INDEX, + Z3_OP_SEQ_TO_RE, + Z3_OP_SEQ_IN_RE, + + // regular expressions + Z3_OP_RE_PLUS, + Z3_OP_RE_STAR, + Z3_OP_RE_OPTION, + Z3_OP_RE_CONCAT, + Z3_OP_RE_UNION, + // Auxiliary Z3_OP_LABEL = 0x700, Z3_OP_LABEL_LIT, @@ -3093,6 +3117,213 @@ extern "C" { /*@}*/ + /** @name Sequences and regular expressions */ + /*@{*/ + + /** + \brief Create a sequence sort out of the sort for the elements. + + def_API('Z3_mk_seq_sort', SORT, (_in(CONTEXT), _in(SORT))) + */ + Z3_sort Z3_API Z3_mk_seq_sort(Z3_context c, Z3_sort s); + + /** + \brief Check if \c s is a sequence sort. + + def_API('Z3_is_seq_sort', BOOL, (_in(CONTEXT), _in(SORT))) + */ + Z3_bool Z3_API Z3_is_seq_sort(Z3_context c, Z3_sort s); + + /** + \brief Create a regular expression sort out of a sequence sort. + + def_API('Z3_mk_re_sort', SORT, (_in(CONTEXT), _in(SORT))) + */ + Z3_sort Z3_API Z3_mk_re_sort(Z3_context c, Z3_sort seq); + + /** + \brief Check if \c s is a regular expression sort. + + def_API('Z3_is_re_sort', BOOL, (_in(CONTEXT), _in(SORT))) + */ + Z3_bool Z3_API Z3_is_re_sort(Z3_context c, Z3_sort s); + + /** + \brief Create a sort for 8 bit strings. + + This function creates a sort for ASCII strings. + Each character is 8 bits. + + def_API('Z3_mk_string_sort', SORT ,(_in(CONTEXT), )) + */ + Z3_sort Z3_API Z3_mk_string_sort(Z3_context c); + + /** + \brief Check if \c s is a string sort. + + def_API('Z3_is_string_sort', BOOL, (_in(CONTEXT), _in(SORT))) + */ + Z3_bool Z3_API Z3_is_string_sort(Z3_context c, Z3_sort s); + + /** + \brief Create a string constant out of the string that is passed in + def_API('Z3_mk_string' ,AST ,(_in(CONTEXT), _in(STRING))) + */ + Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s); + + /** + \brief Determine if \c s is a string constant. + + def_API('Z3_is_string', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_is_string(Z3_context c, Z3_ast s); + + /** + \brief Retrieve the string constant stored in \c s. + + \pre Z3_is_string(c, s) + + def_API('Z3_get_string' ,STRING ,(_in(CONTEXT), _in(AST))) + */ + Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s); + + /** + \brief Create an empty sequence of the sequence sort \c seq. + + \pre s is a sequence sort. + + def_API('Z3_mk_seq_empty' ,AST ,(_in(CONTEXT), _in(SORT))) + */ + Z3_ast Z3_API Z3_mk_seq_empty(Z3_context c, Z3_sort seq); + + /** + \brief Create a unit sequence of \c a. + + def_API('Z3_mk_seq_unit' ,AST ,(_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_unit(Z3_context c, Z3_ast a); + + /** + \brief Concatenate sequences. + + \pre n > 0 + + def_API('Z3_mk_seq_concat' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) + */ + Z3_ast Z3_API Z3_mk_seq_concat(Z3_context c, unsigned n, Z3_ast const args[]); + + /** + \brief Check if \c prefix is a prefix of \c s. + + \pre prefix and s are the same sequence sorts. + + def_API('Z3_mk_seq_prefix' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_prefix(Z3_context c, Z3_ast prefix, Z3_ast s); + + /** + \brief Check if \c suffix is a suffix of \c s. + + \pre \c suffix and \c s are the same sequence sorts. + + def_API('Z3_mk_seq_suffix' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_suffix(Z3_context c, Z3_ast suffix, Z3_ast s); + + /** + \brief Check if \c container contains \c containee. + + \pre \c container and \c containee are the same sequence sorts. + + def_API('Z3_mk_seq_contains' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_contains(Z3_context c, Z3_ast container, Z3_ast containee); + + /** + \brief Extract subsequence starting at \c offset of \c length. + + def_API('Z3_mk_seq_extract' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_extract(Z3_context c, Z3_ast s, Z3_ast offset, Z3_ast length); + + /** + \brief Replace the first occurrence of \c src with \c dst in \c s. + + def_API('Z3_mk_seq_replace' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_replace(Z3_context c, Z3_ast src, Z3_ast dst, Z3_ast s); + + /** + \brief Retrieve from \s the unit sequence positioned at position \c index. + + def_API('Z3_mk_seq_at' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_at(Z3_context c, Z3_ast s, Z3_ast index); + + /** + \brief Return the length of the sequence \c s. + + def_API('Z3_mk_seq_length' ,AST ,(_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_length(Z3_context c, Z3_ast s); + + + /** + \brief Create a regular expression that accepts the sequence \c seq. + + def_API('Z3_mk_seq_to_re' ,AST ,(_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_to_re(Z3_context c, Z3_ast seq); + + /** + \brief Check if \c seq is in the language generated by the regular expression \c re. + + def_API('Z3_mk_seq_in_re' ,AST ,(_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_in_re(Z3_context c, Z3_ast seq, Z3_ast re); + + /** + \brief Create the regular language \c re+. + + def_API('Z3_mk_re_plus' ,AST ,(_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_re_plus(Z3_context c, Z3_ast re); + + /** + \brief Create the regular language \c re*. + + def_API('Z3_mk_re_star' ,AST ,(_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_re_star(Z3_context c, Z3_ast re); + + /** + \brief Create the regular language \c [re]. + + def_API('Z3_mk_re_option' ,AST ,(_in(CONTEXT), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_re_option(Z3_context c, Z3_ast re); + + /** + \brief Create the union of the regular languages. + + \pre n > 0 + + def_API('Z3_mk_re_union' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) + */ + Z3_ast Z3_API Z3_mk_re_union(Z3_context c, unsigned n, Z3_ast const args[]); + + /** + \brief Create the concatenation of the regular languages. + + \pre n > 0 + + def_API('Z3_mk_re_concat' ,AST ,(_in(CONTEXT), _in(UINT), _in_array(1, AST))) + */ + Z3_ast Z3_API Z3_mk_re_concat(Z3_context c, unsigned n, Z3_ast const args[]); + + /*@}*/ + + /** @name Quantifiers */ /*@{*/ /** diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index c3f882e8d..44901b506 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -80,7 +80,6 @@ zstring zstring::replace(zstring const& src, zstring const& dst) const { return result; } -// TBD: SMT-LIB 2.5 strings don't have escape characters other than " static char* esc_table[32] = { "\\0", "^A", "^B", "^C", "^D", "^E", "^F", "\\a", "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V","^W","^X","^Y","^Z","\\e","^\\","^]","^^","^_"}; @@ -404,14 +403,16 @@ sort * seq_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter return m_string; } return m.mk_sort(symbol("Seq"), sort_info(m_family_id, SEQ_SORT, num_parameters, parameters)); - case RE_SORT: + case RE_SORT: { if (num_parameters != 1) { m.raise_exception("Invalid regex sort, expecting one parameter"); } if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { m.raise_exception("invalid regex sort, parameter is not a sort"); } + sort * s = to_sort(parameters[0].get_ast()); return m.mk_sort(symbol("RegEx"), sort_info(m_family_id, RE_SORT, num_parameters, parameters)); + } case _STRING_SORT: return m_string; default: diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index d0a475b25..8cd67a406 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -215,14 +215,13 @@ public: str(seq_util& u): u(u), m(u.m), m_fid(u.m_fid) {} sort* mk_seq(sort* s) { parameter param(s); return m.mk_sort(m_fid, SEQ_SORT, 1, ¶m); } + sort* mk_string_sort() { return m.mk_sort(m_fid, _STRING_SORT, 0, 0); } app* mk_empty(sort* s) { return m.mk_const(m.mk_func_decl(m_fid, OP_SEQ_EMPTY, 0, 0, 0, (expr*const*)0, s)); } app* mk_string(zstring const& s); app* mk_string(symbol const& s) { return u.seq.mk_string(s); } app* mk_char(char ch); app* mk_concat(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); } - app* mk_concat(expr* a, expr* b, expr* c) { - return mk_concat(a, mk_concat(b, c)); - } + app* mk_concat(expr* a, expr* b, expr* c) { return mk_concat(a, mk_concat(b, c)); } expr* mk_concat(unsigned n, expr* const* es) { if (n == 1) return es[0]; SASSERT(n > 1); return m.mk_app(m_fid, OP_SEQ_CONCAT, n, es); } app* mk_length(expr* a) { return m.mk_app(m_fid, OP_SEQ_LENGTH, 1, &a); } app* mk_substr(expr* a, expr* b, expr* c) { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); } @@ -233,8 +232,6 @@ public: app* mk_unit(expr* u) { return m.mk_app(m_fid, OP_SEQ_UNIT, 1, &u); } app* mk_char(zstring const& s, unsigned idx); - - bool is_string(expr const * n) const { return is_app_of(n, m_fid, OP_STRING_CONST); } bool is_string(expr const* n, symbol& s) const { @@ -287,6 +284,8 @@ public: public: re(seq_util& u): m(u.m), m_fid(u.m_fid) {} + sort* mk_re(sort* seq) { parameter param(seq); return m.mk_sort(m_fid, RE_SORT, 1, ¶m); } + app* mk_to_re(expr* s) { return m.mk_app(m_fid, OP_SEQ_TO_RE, 1, &s); } app* mk_in_re(expr* s, expr* r) { return m.mk_app(m_fid, OP_SEQ_IN_RE, s, r); } app* mk_concat(expr* r1, expr* r2) { return m.mk_app(m_fid, OP_RE_CONCAT, r1, r2); } diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index 54cd02832..f64dfd205 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -292,6 +292,8 @@ public: // Generalized: // Src - E -> dst - t -> dst1 => Src - t -> dst1 if dst is final => each Src is final // + // src - e -> dst - ET -> dst1 => src - ET - dst1 if in_degree(dst) = 1, src != dst + // void compress() { for (unsigned i = 0; i < m_delta.size(); ++i) { for (unsigned j = 0; j < m_delta[i].size(); ++j) { @@ -326,6 +328,17 @@ public: add(move(m, src, dst1, t)); remove(dst, dst1, t); } + else if (1 == in_degree(dst) && (!is_final_state(dst) || is_final_state(src)) && init() != dst) { + moves const& mvs = m_delta[dst]; + moves mvs1; + for (unsigned k = 0; k < mvs.size(); ++k) { + mvs1.push_back(move(m, src, mvs[k].dst(), mvs[k].t())); + } + for (unsigned k = 0; k < mvs1.size(); ++k) { + remove(dst, mvs1[k].dst(), mvs1[k].t()); + add(mvs1[k]); + } + } else if (false && 1 == out_degree(dst) && all_epsilon_in(dst) && init() != dst && !is_final_state(dst)) { move const& mv = m_delta[dst][0]; T* t = mv.t(); @@ -364,7 +377,7 @@ public: } } } - + bool is_sequence(unsigned& length) const { if (is_final_state(m_init) && (out_degree(m_init) == 0 || (out_degree(m_init) == 1 && is_loop_state(m_init)))) { length = 0; @@ -466,6 +479,14 @@ public: } private: + void remove_dead_states() { + unsigned_vector remap; + for (unsigned i = 0; i < m_delta.size(); ++i) { + + } + } + + void add(move const& mv) { m_delta[mv.src()].push_back(mv); m_delta_inv[mv.dst()].push_back(mv); diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index ed16003c2..3664ed903 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -169,24 +169,16 @@ namespace smt2 { char c = curr(); if (c == EOF) throw scanner_exception("unexpected end of string", m_line, m_spos); - if (c == '\"') { + if (c == '\n') { + new_line(); + } + else if (c == '\"') { next(); if (curr() != '\"') { m_string.push_back(0); return STRING_TOKEN; } } - else if (c == '\n') { - new_line(); - } - else if (c == '\\') { - next(); - c = curr(); - if (c == EOF) - throw scanner_exception("unexpected end of string", m_line, m_spos); - if (c != '\\' && c != '\"') - throw scanner_exception("invalid escape sequence", m_line, m_spos); - } m_string.push_back(c); next(); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index c271e405e..1f3afa672 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -199,7 +199,7 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>solve_eqs\n";); return FC_CONTINUE; } - if (solve_nqs()) { + if (solve_nqs(0)) { ++m_stats.m_solve_nqs; TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; @@ -802,21 +802,20 @@ bool theory_seq::solve_binary_eq(expr* l, expr* r, dependency* dep) { return false; } -bool theory_seq::solve_nqs() { +bool theory_seq::solve_nqs(unsigned i) { bool change = false; context & ctx = get_context(); - for (unsigned i = 0; !ctx.inconsistent() && i < m_nqs.size(); ++i) { + for (; !ctx.inconsistent() && i < m_nqs.size(); ++i) { if (!m_nqs[i].is_solved()) { - change = solve_ne(i) || change; + solve_ne(i); } } - return change || ctx.inconsistent(); + return m_new_propagation || ctx.inconsistent(); } -bool theory_seq::solve_ne(unsigned idx) { +void theory_seq::solve_ne(unsigned idx) { context& ctx = get_context(); seq_rewriter rw(m); - bool change = false; ne const& n = m_nqs[idx]; TRACE("seq", display_disequation(tout, n);); @@ -827,7 +826,7 @@ bool theory_seq::solve_ne(unsigned idx) { case l_false: // mark as solved in mark_solved(idx); - return false; + return; case l_true: break; case l_undef: @@ -844,7 +843,7 @@ bool theory_seq::solve_ne(unsigned idx) { expr_ref rh = canonize(r, deps); if (!rw.reduce_eq(lh, rh, lhs, rhs)) { mark_solved(idx); - return change; + return; } else if (unchanged(l, lhs, r, rhs) ) { // continue @@ -870,11 +869,12 @@ bool theory_seq::solve_ne(unsigned idx) { switch (ctx.get_assignment(lit)) { case l_false: mark_solved(idx); - return false; + return; case l_true: break; case l_undef: ++num_undef_lits; + m_new_propagation = true; break; } } @@ -882,16 +882,14 @@ bool theory_seq::solve_ne(unsigned idx) { m_trail_stack.push(push_dep(*this, idx, deps)); erase_index(idx, i); --i; - change = true; } } if (num_undef_lits == 0 && n.m_lhs.empty()) { literal_vector lits(n.m_lits); lits.push_back(~mk_eq(n.m_l, n.m_r, false)); set_conflict(n.m_dep, lits); - return true; + SASSERT(m_new_propagation); } - return change; } @@ -986,7 +984,6 @@ void theory_seq::display(std::ostream & out) const { display_equations(out); } if (m_nqs.size() > 0) { - out << "Disequations:\n"; display_disequations(out); } if (!m_re2aut.empty()) { @@ -1017,8 +1014,13 @@ void theory_seq::display_equations(std::ostream& out) const { } void theory_seq::display_disequations(std::ostream& out) const { + bool first = true; for (unsigned i = 0; i < m_nqs.size(); ++i) { - display_disequation(out, m_nqs[i]); + if (!m_nqs[i].is_solved()) { + if (first) out << "Disequations:\n"; + first = false; + display_disequation(out, m_nqs[i]); + } } } @@ -1155,7 +1157,7 @@ app* theory_seq::mk_value(app* e) { unsigned sz; if (bv.is_numeral(result, val, sz) && sz == zstring().num_bits()) { unsigned v = val.get_unsigned(); - if ((0 <= v && v < 32) || v == 127) { + if ((0 <= v && v < 7) || (14 <= v && v < 32) || v == 127) { result = m_util.str.mk_unit(result); } else { @@ -1961,6 +1963,7 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { m_rewrite(eq); if (!m.is_false(eq)) { m_nqs.push_back(ne(e1, e2)); + solve_nqs(m_nqs.size() - 1); } // add solution for variable that is non-empty? } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 78eebe301..db5fa0f8b 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -357,8 +357,8 @@ namespace smt { bool is_binary_eq(expr* l, expr* r, expr*& x, ptr_vector& xunits, ptr_vector& yunits, expr*& y); bool solve_binary_eq(expr* l, expr* r, dependency* dep); - bool solve_nqs(); - bool solve_ne(unsigned i); + bool solve_nqs(unsigned i); + void solve_ne(unsigned i); bool unchanged(expr* e, expr_ref_vector& es) const { return es.size() == 1 && es[0] == e; } bool unchanged(expr* e, expr_ref_vector& es, expr* f, expr_ref_vector& fs) const { return From 1147037a990022ee8e1e962facae67153d565765 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Jan 2016 22:54:49 -0800 Subject: [PATCH 25/35] seq API Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 507e9bfee..802108163 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -9015,12 +9015,13 @@ def Empty(s): >>> print(e) "" >>> e2 = String("") - >>> print(e == e2) + >>> print(e.eq(e2)) True >>> e3 = Empty(SeqSort(IntSort())) >>> print(e3) + seq.empty """ - return SeqRef(Z3_mk_seq_empty(s.ctx_ref(), s.as_ast()), s.ctx) + return SeqRef(Z3_mk_seq_empty(s.ctx_ref(), s.ast), s.ctx) def Unit(a): """Create a singleton sequence""" From b5969326bc5a771f9739ac6a8087e41fd6eb7b87 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 2 Jan 2016 23:31:36 -0800 Subject: [PATCH 26/35] seq API Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 129 ++++++++++++++++++++++++------ src/api/z3_api.h | 2 +- src/ast/rewriter/seq_rewriter.cpp | 3 +- 3 files changed, 107 insertions(+), 27 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 802108163..409d511ef 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -8959,11 +8959,8 @@ class SeqRef(ExprRef): def sort(self): return SeqSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) - def __add__(self, other): - v = (Ast * 2)() - v[0] = self.as_ast() - v[1] = other.as_ast() - return SeqRef(Z3_mk_seq_concat(self.ctx_ref(), 2, v), self.ctx) + def __add__(self, other): + return Concat(self, other) def __getitem__(self, i): return SeqRef(Z3_mk_seq_at(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) @@ -8985,23 +8982,36 @@ def _coerce_seq(s, ctx=None): s = String(s, ctx) return s -def _get_ctx2(a, b): +def _get_ctx2(a, b, ctx=None): if is_expr(a): return a.ctx if is_expr(b): return b.ctx - return None + return ctx def is_seq(a): - """Return `True` if `a` is a Z3 sequence expression.""" + """Return `True` if `a` is a Z3 sequence expression. + >>> print (is_seq(Unit(IntVal(0)))) + True + >>> print (is_seq(String("abc"))) + True + """ return isinstance(a, SeqRef) def is_string_sort(a): - """Return `True` if `a` is a Z3 string expression.""" + """Return `True` if `a` is a Z3 string expression. + >>> print (is_string_sort(String("ab"))) + True + """ return isinstance(a, SeqRef) and a.is_string_sort() def is_string_value(a): - """return 'True' if 'a' is a Z3 string constant expression.""" + """return 'True' if 'a' is a Z3 string constant expression. + >>> print (is_string_value(String("a"))) + True + >>> print (is_string_value(String("a") + String("b"))) + False + """ return isinstance(a, SeqRef) and a.is_string_value() def String(s, ctx=None): @@ -9028,21 +9038,42 @@ def Unit(a): return SeqRef(Z3_mk_seq_unit(a.ctx_ref(), a.as_ast()), a.ctx) def PrefixOf(a, b): - """Check if 'a' is a prefix of 'b'""" + """Check if 'a' is a prefix of 'b' + >>> s1 = PrefixOf("ab", "abc") + >>> print (simplify(s1)) + True + >>> s2 = PrefixOf("bc", "abc") + >>> print (simplify(s2)) + False + """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) b = _coerce_seq(b, ctx) return BoolRef(Z3_mk_seq_prefix(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def SuffixOf(a, b): - """Check if 'a' is a suffix of 'b'""" + """Check if 'a' is a suffix of 'b' + >>> s1 = SuffixOf("ab", "abc") + >>> print (simplify(s1)) + False + >>> s2 = SuffixOf("bc", "abc") + >>> print (simplify(s2)) + True + """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) b = _coerce_seq(b, ctx) return BoolRef(Z3_mk_seq_suffix(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) def Contains(a, b): - """Check if 'a' contains 'b'""" + """Check if 'a' contains 'b' + >>> s1 = Contains("abc", "ab") + >>> print (simplify(s1)) + True + >>> s2 = SuffixOf("abc", "bc") + >>> print (simplify(s2)) + True + """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) b = _coerce_seq(b, ctx) @@ -9052,20 +9083,34 @@ def Contains(a, b): # """Extract a sequence at offset with indicated length""" # return SeqRef(Z3_mk_seq_extract(a.ctx_ref(), a.as_ast(), offset.as_ast()), length.ctx) -def Replace(src, dst, s): - """Replace the first occurrence of 'src' by 'dst' in 's'""" - ctx = _get_ctx2(src, _get_ctx2(dst, s)) +def Replace(s, src, dst): + """Replace the first occurrence of 'src' by 'dst' in 's' + >>> r = Replace("aaa", "a", "b") + >>> print (simplify(r)) + "baa" + """ + ctx = _get_ctx2(dst, s) + if ctx is None and is_expr(src): + ctx = src.ctx src = _coerce_seq(src, ctx) dst = _coerce_seq(dst, ctx) s = _coerce_seq(s, ctx) - return SeqRef(Z3_mk_seq_replace(src.ctx_ref(), src.as_ast(), dst.as_ast()), s.ctx) + return SeqRef(Z3_mk_seq_replace(src.ctx_ref(), s.as_ast(), src.as_ast(), dst.as_ast()), s.ctx) def Length(s): - """Obtain the length of a sequence 's'""" - return ArithRef(Z3_mk_seq_length(s.ctx_ref(), s.as_ast(), s.ctx)) + """Obtain the length of a sequence 's' + >>> l = Length(String("abc")) + >>> print (simplify(l)) + 3 + """ + return ArithRef(Z3_mk_seq_length(s.ctx_ref(), s.as_ast()), s.ctx) def Re(s, ctx=None): - """The regular expression that accepts sequence 's'""" + """The regular expression that accepts sequence 's' + >>> s1 = Re("ab") + >>> s2 = Re(String("ab")) + >>> s3 = Re(Unit(BoolVal(True))) + """ s = _coerce_seq(s, ctx) return ReRef(Z3_mk_seq_to_re(s.ctx_ref(), s.as_ast()), s.ctx) @@ -9090,20 +9135,45 @@ def ReSort(s): class ReRef(ExprRef): """Regular expressions.""" - def __add__(self, other): - v = (Ast * 2)() - v[0] = self.as_ast() - v[1] = other.as_ast() - return SeqRef(Z3_mk_re_union(self.ctx_ref(), 2, v), self.ctx) + def __add__(self, other): + return Union(self, other) def is_re(s): return isinstance(s, ReRef) + def InRe(s, re): + """Create regular expression membership test + >>> re = Union(Re("a"),Re("b")) + >>> print (simplify(InRe("a", re))) + True + >>> print (simplify(InRe("b", re))) + True + >>> print (simplify(InRe("c", re))) + False + """ s = _coerce_seq(s, re.ctx) return BoolRef(Z3_mk_seq_in_re(s.ctx_ref(), s.as_ast(), re.as_ast()), s.ctx) +def Union(*args): + """Create union of regular expressions. + >>> re = Union(Re("a"), Re("b"), Re("c")) + >>> print (simplify(InRe("d", re))) + False + """ + args = _get_args(args) + sz = len(args) + if __debug__: + _z3_assert(sz >= 2, "At least two arguments expected.") + _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") + ctx = args[0].ctx + v = (Ast * sz)() + for i in range(sz): + v[i] = args[i].as_ast() + return ReRef(Z3_mk_re_union(ctx.ref(), sz, v), ctx) + + def Plus(re): """Create the regular expression accepting one or more repetitions of argument. >>> re = Plus(Re("a")) @@ -9117,6 +9187,15 @@ def Plus(re): return ReRef(Z3_mk_re_plus(re.ctx_ref(), re.as_ast()), re.ctx) def Option(re): + """Create the regular expression that optionally accepts the argument. + >>> re = Option(Re("a")) + >>> print(simplify(InRe("a", re))) + True + >>> print(simplify(InRe("", re))) + True + >>> print(simplify(InRe("aa", re))) + False + """ return ReRef(Z3_mk_re_option(re.ctx_ref(), re.as_ast()), re.ctx) def Star(re): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index e00134c06..fab272499 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3251,7 +3251,7 @@ extern "C" { def_API('Z3_mk_seq_replace' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) */ - Z3_ast Z3_API Z3_mk_seq_replace(Z3_context c, Z3_ast src, Z3_ast dst, Z3_ast s); + Z3_ast Z3_API Z3_mk_seq_replace(Z3_context c, Z3_ast s, Z3_ast src, Z3_ast dst); /** \brief Retrieve from \s the unit sequence positioned at position \c index. diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 2db2391d9..8f6aa6fca 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -822,7 +822,8 @@ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) { sort* s; VERIFY(m_util.is_re(a, s)); - result = m_util.re.mk_union(m_util.re.mk_to_re(m_util.str.mk_empty(s)), a); + sort_ref seq(m_util.str.mk_seq(s), m()); + result = m_util.re.mk_union(m_util.re.mk_to_re(m_util.str.mk_empty(seq)), a); return BR_REWRITE1; } From 532ec6f8dc2a60d2f468218eacf69983ce3e5b09 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Jan 2016 14:07:34 -0800 Subject: [PATCH 27/35] seq API Signed-off-by: Nikolaj Bjorner --- src/api/api_seq.cpp | 2 +- src/api/python/z3.py | 76 +++++++++++++++++++++++++++++++------------- src/api/z3_api.h | 9 ++++++ 3 files changed, 64 insertions(+), 23 deletions(-) diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 0949cc2e4..5704ffdb0 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -135,7 +135,7 @@ extern "C" { MK_TERNARY(Z3_mk_seq_replace, mk_c(c)->get_seq_fid(), OP_SEQ_REPLACE, SKIP); MK_BINARY(Z3_mk_seq_at, mk_c(c)->get_seq_fid(), OP_SEQ_AT, SKIP); MK_UNARY(Z3_mk_seq_length, mk_c(c)->get_seq_fid(), OP_SEQ_LENGTH, SKIP); - //MK_BINARY(Z3_mk_seq_index, mk_c(c)->get_seq_fid(), OP_SEQ_INDEX, SKIP); + MK_TERNARY(Z3_mk_seq_index, mk_c(c)->get_seq_fid(), OP_SEQ_INDEX, SKIP); MK_UNARY(Z3_mk_seq_to_re, mk_c(c)->get_seq_fid(), OP_SEQ_TO_RE, SKIP); MK_BINARY(Z3_mk_seq_in_re, mk_c(c)->get_seq_fid(), OP_SEQ_IN_RE, SKIP); diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 409d511ef..367f8ea0f 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -3596,14 +3596,26 @@ def Concat(*args): return r def Extract(high, low, a): - """Create a Z3 bit-vector extraction expression. + """Create a Z3 bit-vector extraction expression, or create a string extraction expression. >>> x = BitVec('x', 8) >>> Extract(6, 2, x) Extract(6, 2, x) >>> Extract(6, 2, x).sort() BitVec(5) + >>> simplify(Extract(StringVal("abcd"),2,1)) + "c" """ + if isinstance(high, str): + high = StringVal(high) + if is_seq(high): + s = high + offset = _py2expr(low, high.ctx) + length = _py2expr(a, high.ctx) + + if __debug__: + _z3_assert(is_int(offset) and is_int(length), "Second and third arguments must be integers") + return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) if __debug__: _z3_assert(low <= high, "First argument must be greater than or equal to second argument") _z3_assert(isinstance(high, int) and high >= 0 and isinstance(low, int) and low >= 0, "First and second arguments must be non negative integers") @@ -8979,7 +8991,7 @@ class SeqRef(ExprRef): def _coerce_seq(s, ctx=None): if isinstance(s, str): ctx = _get_ctx(ctx) - s = String(s, ctx) + s = StringVal(s, ctx) return s def _get_ctx2(a, b, ctx=None): @@ -8987,34 +8999,36 @@ def _get_ctx2(a, b, ctx=None): return a.ctx if is_expr(b): return b.ctx + if ctx is None: + ctx = main_ctx() return ctx def is_seq(a): """Return `True` if `a` is a Z3 sequence expression. >>> print (is_seq(Unit(IntVal(0)))) True - >>> print (is_seq(String("abc"))) + >>> print (is_seq(StringVal("abc"))) True """ return isinstance(a, SeqRef) def is_string_sort(a): """Return `True` if `a` is a Z3 string expression. - >>> print (is_string_sort(String("ab"))) + >>> print (is_string_sort(StringVal("ab"))) True """ return isinstance(a, SeqRef) and a.is_string_sort() def is_string_value(a): """return 'True' if 'a' is a Z3 string constant expression. - >>> print (is_string_value(String("a"))) + >>> print (is_string_value(StringVal("a"))) True - >>> print (is_string_value(String("a") + String("b"))) + >>> print (is_string_value(StringVal("a") + StringVal("b"))) False """ return isinstance(a, SeqRef) and a.is_string_value() -def String(s, ctx=None): +def StringVal(s, ctx=None): """create a string expression""" ctx = _get_ctx(ctx) return SeqRef(Z3_mk_string(ctx.ref(), s), ctx) @@ -9024,7 +9038,7 @@ def Empty(s): >>> e = Empty(StringSort()) >>> print(e) "" - >>> e2 = String("") + >>> e2 = StringVal("") >>> print(e.eq(e2)) True >>> e3 = Empty(SeqSort(IntSort())) @@ -9040,10 +9054,10 @@ def Unit(a): def PrefixOf(a, b): """Check if 'a' is a prefix of 'b' >>> s1 = PrefixOf("ab", "abc") - >>> print (simplify(s1)) + >>> simplify(s1) True >>> s2 = PrefixOf("bc", "abc") - >>> print (simplify(s2)) + >>> simplify(s2) False """ ctx = _get_ctx2(a, b) @@ -9054,10 +9068,10 @@ def PrefixOf(a, b): def SuffixOf(a, b): """Check if 'a' is a suffix of 'b' >>> s1 = SuffixOf("ab", "abc") - >>> print (simplify(s1)) + >>> simplify(s1) False >>> s2 = SuffixOf("bc", "abc") - >>> print (simplify(s2)) + >>> simplify(s2) True """ ctx = _get_ctx2(a, b) @@ -9068,10 +9082,10 @@ def SuffixOf(a, b): def Contains(a, b): """Check if 'a' contains 'b' >>> s1 = Contains("abc", "ab") - >>> print (simplify(s1)) + >>> simplify(s1) True - >>> s2 = SuffixOf("abc", "bc") - >>> print (simplify(s2)) + >>> s2 = Contains("abc", "bc") + >>> simplify(s2) True """ ctx = _get_ctx2(a, b) @@ -9079,14 +9093,11 @@ def Contains(a, b): b = _coerce_seq(b, ctx) return BoolRef(Z3_mk_seq_contains(a.ctx_ref(), a.as_ast(), b.as_ast()), a.ctx) -#def Extract(a, offset, length): -# """Extract a sequence at offset with indicated length""" -# return SeqRef(Z3_mk_seq_extract(a.ctx_ref(), a.as_ast(), offset.as_ast()), length.ctx) def Replace(s, src, dst): """Replace the first occurrence of 'src' by 'dst' in 's' >>> r = Replace("aaa", "a", "b") - >>> print (simplify(r)) + >>> simplify(r) "baa" """ ctx = _get_ctx2(dst, s) @@ -9097,18 +9108,39 @@ def Replace(s, src, dst): s = _coerce_seq(s, ctx) return SeqRef(Z3_mk_seq_replace(src.ctx_ref(), s.as_ast(), src.as_ast(), dst.as_ast()), s.ctx) +def IndexOf(s, substr): + return IndexOf(s, substr, IntVal(0)) + +def IndexOf(s, substr, offset): + """Retrieve the index of substring within a string starting at a specified offset. + >>> simplify(IndexOf("abcabc", "bc", 0)) + 1 + >>> simplify(IndexOf("abcabc", "bc", 2)) + 4 + """ + ctx = None + if is_expr(offset): + ctx = offset.ctx + ctx = _get_ctx2(s, substr, ctx) + s = _coerce_seq(s, ctx) + substr = _coerce_seq(substr, ctx) + if isinstance(offset, int): + offset = IntVal(offset, ctx) + return SeqRef(Z3_mk_seq_index(s.ctx_ref(), s.as_ast(), substr.as_ast(), offset.as_ast()), s.ctx) + def Length(s): """Obtain the length of a sequence 's' - >>> l = Length(String("abc")) - >>> print (simplify(l)) + >>> l = Length(StringVal("abc")) + >>> simplify(l) 3 """ + s = _coerce_seq(s) return ArithRef(Z3_mk_seq_length(s.ctx_ref(), s.as_ast()), s.ctx) def Re(s, ctx=None): """The regular expression that accepts sequence 's' >>> s1 = Re("ab") - >>> s2 = Re(String("ab")) + >>> s2 = Re(StringVal("ab")) >>> s3 = Re(Unit(BoolVal(True))) """ s = _coerce_seq(s, ctx) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index fab272499..528399394 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -3268,6 +3268,15 @@ extern "C" { Z3_ast Z3_API Z3_mk_seq_length(Z3_context c, Z3_ast s); + /** + \brief Return index of first occurrence of \c substr in \c s starting from offset \c offset. + If \c s does not contain \c substr, then the value is -1, if \c offset is the length of \c s, then the value is -1 as well. + The function is under-specified if \c offset is negative or larger than the length of \c s. + + def_API('Z3_mk_seq_index' ,AST ,(_in(CONTEXT), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_seq_index(Z3_context c, Z3_ast s, Z3_ast substr, Z3_ast offset); + /** \brief Create a regular expression that accepts the sequence \c seq. From 8e80fb830bc81b50da43d3257f0ac43f7d048994 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Jan 2016 14:12:45 -0800 Subject: [PATCH 28/35] merge fixes Signed-off-by: Nikolaj Bjorner --- src/util/mpf.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 6cf8e3a4d..06a82b0d5 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -239,22 +239,15 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode else { scoped_mpq sig(m_mpq_manager); scoped_mpz exp(m_mpq_manager); - scoped_mpq pow(m_mpq_manager); m_mpq_manager.set(sig, significand); m_mpq_manager.abs(sig); m_mpz_manager.set(exp, exponent); - m_mpq_manager.set(pow, mpq(2)); - + // Normalize - unsigned loop = 0; - while (m_mpq_manager.ge(sig, pow)) { - m_mpq_manager.mul(pow, 2, pow); + while (m_mpq_manager.ge(sig, 2)) { + m_mpq_manager.div(sig, mpq(2), sig); m_mpz_manager.inc(exp); - ++loop; - } - if (loop > 0) { - m_mpq_manager.div(sig, pow, sig); } while (m_mpq_manager.lt(sig, 1)) { From a3c4972c85b33cd124b4a487eaa10e6e06a853e3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Jan 2016 17:16:13 -0800 Subject: [PATCH 29/35] seq API, tuning Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 184 +++++++++++++++++++++++++++++++++- src/math/automata/automaton.h | 35 +++++-- src/smt/theory_seq.cpp | 32 +++++- 3 files changed, 237 insertions(+), 14 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 1635ea7bb..b353499ad 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -205,6 +205,18 @@ namespace z3 { \brief Return the Bit-vector sort of size \c sz. That is, the sort for bit-vectors of size \c sz. */ sort bv_sort(unsigned sz); + /** + \brief Return the sort for ASCII strings. + */ + sort string_sort(); + /** + \brief Return a sequence sort over base sort \c s. + */ + sort seq_sort(sort& s); + /** + \brief Return a regular expression sort over sequences \c seq_sort. + */ + sort re_sort(sort& seq_sort); /** \brief Return an array sort for arrays from \c d to \c r. @@ -261,6 +273,9 @@ namespace z3 { expr bv_val(__uint64 n, unsigned sz); expr bv_val(char const * n, unsigned sz); + expr string_val(char const* s); + expr string_val(std::string const& s); + expr num_val(int n, sort const & s); /** @@ -425,6 +440,14 @@ namespace z3 { \brief Return true if this sort is a Relation sort. */ bool is_relation() const { return sort_kind() == Z3_RELATION_SORT; } + /** + \brief Return true if this sort is a Sequence sort. + */ + bool is_seq() const { return sort_kind() == Z3_SEQ_SORT; } + /** + \brief Return true if this sort is a regular expression sort. + */ + bool is_re() const { return sort_kind() == Z3_RE_SORT; } /** \brief Return true if this sort is a Finite domain sort. */ @@ -532,6 +555,15 @@ namespace z3 { \brief Return true if this is a Relation expression. */ bool is_relation() const { return get_sort().is_relation(); } + /** + \brief Return true if this is a sequence expression. + */ + bool is_seq() const { return get_sort().is_seq(); } + /** + \brief Return true if this is a regular expression. + */ + bool is_re() const { return get_sort().is_re(); } + /** \brief Return true if this is a Finite-domain expression. @@ -663,6 +695,7 @@ namespace z3 { friend expr distinct(expr_vector const& args); friend expr concat(expr const& a, expr const& b); + friend expr concat(expr_vector const& args); friend expr operator==(expr const & a, expr const & b); friend expr operator==(expr const & a, int b); @@ -728,10 +761,50 @@ namespace z3 { friend expr operator|(int a, expr const & b); friend expr operator~(expr const & a); - expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); return expr(ctx(), r); } + expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); ctx().check_error(); return expr(ctx(), r); } unsigned lo() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 1)); } unsigned hi() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 0)); } + /** + \brief sequence and regular expression operations. + + is overloaeded as sequence concatenation and regular expression union. + concat is overloaded to handle sequences and regular expressions + */ + expr extract(expr const& offset, expr const& length) const { + check_context(*this, offset); check_context(offset, length); + Z3_ast r = Z3_mk_seq_extract(ctx(), *this, offset, length); check_error(); return expr(ctx(), r); + } + expr replace(expr const& src, expr const& dst) const { + check_context(*this, src); check_context(src, dst); + Z3_ast r = Z3_mk_seq_replace(ctx(), *this, src, dst); + check_error(); + return expr(ctx(), r); + } + expr unit() const { + Z3_ast r = Z3_mk_seq_unit(ctx(), *this); + check_error(); + return expr(ctx(), r); + } + expr contains(expr const& s) { + check_context(*this, s); + Z3_ast r = Z3_mk_seq_contains(ctx(), *this, s); + check_error(); + return expr(ctx(), r); + } + expr at(expr const& index) const { + check_context(*this, index); + Z3_ast r = Z3_mk_seq_at(ctx(), *this, index); + check_error(); + return expr(ctx(), r); + } + expr length() const { + Z3_ast r = Z3_mk_seq_length(ctx(), *this); + check_error(); + return expr(ctx(), r); + } + + + /** \brief Return a simplified version of this expression. */ @@ -835,6 +908,13 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvadd(a.ctx(), a, b); } + else if (a.is_seq() && b.is_seq()) { + return concat(a, b); + } + else if (a.is_re() && b.is_re()) { + Z3_ast _args[2] = { a, b }; + r = Z3_mk_re_union(a.ctx(), 2, _args); + } else { // operator is not supported by given arguments. assert(false); @@ -1219,11 +1299,48 @@ namespace z3 { inline expr concat(expr const& a, expr const& b) { check_context(a, b); - Z3_ast r = Z3_mk_concat(a.ctx(), a, b); + Z3_ast r; + if (Z3_is_seq_sort(a.ctx(), a.get_sort())) { + Z3_ast _args[2] = { a, b }; + r = Z3_mk_seq_concat(a.ctx(), 2, _args); + } + else if (Z3_is_re_sort(a.ctx(), a.get_sort())) { + Z3_ast _args[2] = { a, b }; + r = Z3_mk_re_concat(a.ctx(), 2, _args); + } + else { + r = Z3_mk_concat(a.ctx(), a, b); + } a.ctx().check_error(); return expr(a.ctx(), r); } + inline expr concat(expr_vector const& args) { + Z3_ast r; + assert(args.size() > 0); + if (args.size() == 1) { + return args[0]; + } + context& ctx = args[0].ctx(); + array _args(args); + if (Z3_is_seq_sort(ctx, args[0].get_sort())) { + r = Z3_mk_seq_concat(ctx, _args.size(), _args.ptr()); + } + else if (Z3_is_re_sort(ctx, args[0].get_sort())) { + r = Z3_mk_re_concat(ctx, _args.size(), _args.ptr()); + } + else { + r = _args[args.size()-1]; + for (unsigned i = args.size()-1; i > 0; ) { + --i; + r = Z3_mk_concat(ctx, _args[i], r); + ctx.check_error(); + } + } + ctx.check_error(); + return expr(ctx, r); + } + class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { @@ -1762,6 +1879,10 @@ namespace z3 { inline sort context::int_sort() { Z3_sort s = Z3_mk_int_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::real_sort() { Z3_sort s = Z3_mk_real_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::bv_sort(unsigned sz) { Z3_sort s = Z3_mk_bv_sort(m_ctx, sz); check_error(); return sort(*this, s); } + inline sort context::string_sort() { Z3_sort s = Z3_mk_string_sort(m_ctx); check_error(); return sort(*this, s); } + inline sort context::seq_sort(sort& s) { Z3_sort r = Z3_mk_seq_sort(m_ctx, s); check_error(); return sort(*this, r); } + inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); } + inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); @@ -1885,6 +2006,9 @@ namespace z3 { inline expr context::bv_val(__uint64 n, unsigned sz) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } inline expr context::bv_val(char const * n, unsigned sz) { Z3_ast r = Z3_mk_numeral(m_ctx, n, bv_sort(sz)); 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::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr func_decl::operator()(unsigned n, expr const * args) const { @@ -2017,6 +2141,62 @@ namespace z3 { d.check_error(); return expr(d.ctx(), r); } + + // sequence and regular expression operations. + // union is + + // concat is overloaded to handle sequences and regular expressions + + inline expr empty(sort const& s) { + Z3_ast r = Z3_mk_seq_empty(s.ctx(), s); + s.check_error(); + return expr(s.ctx(), r); + } + inline expr suffixof(expr const& a, expr const& b) { + check_context(a, b); + Z3_ast r = Z3_mk_seq_suffix(a.ctx(), a, b); + a.check_error(); + return expr(a.ctx(), r); + } + inline expr prefixof(expr const& a, expr const& b) { + check_context(a, b); + Z3_ast r = Z3_mk_seq_prefix(a.ctx(), a, b); + a.check_error(); + return expr(a.ctx(), r); + } + inline expr indexof(expr const& s, expr const& substr, expr const& offset) { + check_context(s, substr); check_context(s, offset); + Z3_ast r = Z3_mk_seq_index(s.ctx(), s, substr, offset); + s.check_error(); + return expr(s.ctx(), r); + } + inline expr to_re(expr const& s) { + Z3_ast r = Z3_mk_seq_to_re(s.ctx(), s); + s.check_error(); + return expr(s.ctx(), r); + } + inline expr in_re(expr const& s, expr const& re) { + check_context(s, re); + Z3_ast r = Z3_mk_seq_in_re(s.ctx(), s, re); + s.check_error(); + return expr(s.ctx(), r); + } + inline expr plus(expr const& re) { + Z3_ast r = Z3_mk_re_plus(re.ctx(), re); + re.check_error(); + return expr(re.ctx(), r); + } + inline expr option(expr const& re) { + Z3_ast r = Z3_mk_re_option(re.ctx(), re); + re.check_error(); + return expr(re.ctx(), r); + } + inline expr star(expr const& re) { + Z3_ast r = Z3_mk_re_star(re.ctx(), re); + re.check_error(); + return expr(re.ctx(), r); + } + + inline expr interpolant(expr const& a) { return expr(a.ctx(), Z3_mk_interpolant(a.ctx(), a)); } diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index f64dfd205..2a4c964f9 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -292,7 +292,8 @@ public: // Generalized: // Src - E -> dst - t -> dst1 => Src - t -> dst1 if dst is final => each Src is final // - // src - e -> dst - ET -> dst1 => src - ET - dst1 if in_degree(dst) = 1, src != dst + // src - e -> dst - ET -> Dst1 => src - ET -> Dst1 if in_degree(dst) = 1, src != dst + // Src - E -> dst - et -> dst1 => Src - et -> dst1 if out_degree(dst) = 1, src != dst // void compress() { for (unsigned i = 0; i < m_delta.size(); ++i) { @@ -339,24 +340,42 @@ public: add(mvs1[k]); } } - else if (false && 1 == out_degree(dst) && all_epsilon_in(dst) && init() != dst && !is_final_state(dst)) { + // + // Src - E -> dst - et -> dst1 => Src - et -> dst1 if out_degree(dst) = 1, src != dst + // + else if (1 == out_degree(dst) && all_epsilon_in(dst) && init() != dst && !is_final_state(dst)) { move const& mv = m_delta[dst][0]; - T* t = mv.t(); unsigned dst1 = mv.dst(); + T* t = mv.t(); unsigned_vector src0s; moves const& mvs = m_delta_inv[dst]; + moves mvs1; for (unsigned k = 0; k < mvs.size(); ++k) { SASSERT(mvs[k].is_epsilon()); - src0s.push_back(mvs[k].src()); + mvs1.push_back(move(m, mvs[k].src(), dst1, t)); } - for (unsigned k = 0; k < src0s.size(); ++k) { - remove(src0s[k], dst, 0); - add(move(m, src0s[i], dst1, t)); + for (unsigned k = 0; k < mvs1.size(); ++k) { + remove(mvs1[k].src(), dst, 0); + add(mvs1[k]); } remove(dst, dst1, t); --j; continue; } + // + // Src1 - ET -> src - e -> dst => Src1 - ET -> dst if out_degree(src) = 1, src != init() + // + else if (1 == out_degree(src) && init() != src && (!is_final_state(src) || is_final_state(dst))) { + moves const& mvs = m_delta_inv[src]; + moves mvs1; + for (unsigned k = 0; k < mvs.size(); ++k) { + mvs1.push_back(move(m, mvs[k].src(), dst, mvs[k].t())); + } + for (unsigned k = 0; k < mvs1.size(); ++k) { + remove(mvs1[k].src(), src, mvs1[k].t()); + add(mvs1[k]); + } + } else { continue; } @@ -482,7 +501,7 @@ private: void remove_dead_states() { unsigned_vector remap; for (unsigned i = 0; i < m_delta.size(); ++i) { - + } } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 1f3afa672..88988d2b4 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -373,14 +373,12 @@ bool theory_seq::propagate_length_coherence(expr* e) { expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); - m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); } - + m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); return true; } bool theory_seq::check_length_coherence() { - bool coherent = true; obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); for (; it != end; ++it) { expr* e = *it; @@ -398,7 +396,7 @@ bool theory_seq::check_length_coherence() { return false; } } - return coherent; + return true; } /* @@ -890,6 +888,23 @@ void theory_seq::solve_ne(unsigned idx) { set_conflict(n.m_dep, lits); SASSERT(m_new_propagation); } + if (num_undef_lits == 0 && n.m_lhs.size() == 1) { + expr* l = n.m_lhs[0]; + expr* r = n.m_rhs[0]; + if (m_util.str.is_empty(r)) { + std::swap(l, r); + } + if (m_util.str.is_empty(l) && is_var(r)) { + literal lit = ~mk_eq_empty(r); + if (ctx.get_assignment(lit) == l_true) { + expr_ref head(m), tail(m); + mk_decompose(r, head, tail); + expr_ref conc(m_util.str.mk_concat(head, tail), m); + propagate_is_conc(r, conc); + } + m_new_propagation = true; + } + } } @@ -2171,6 +2186,15 @@ bool theory_seq::add_accept2step(expr* acc) { break; } } + if (has_undef && mvs.size() == 1) { + literal lit = lits.back(); + lits.pop_back(); + for (unsigned i = 0; i < lits.size(); ++i) { + lits[i].neg(); + } + propagate_lit(0, lits.size(), lits.c_ptr(), lit); + return false; + } if (has_undef) { return true; } From 68a532d066f411789721f2f61dd8ad3273594c82 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 3 Jan 2016 20:53:06 -0800 Subject: [PATCH 30/35] seq, API Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Context.cs | 244 ++++++++++++++++++++++++++++++++++++++ src/api/dotnet/Expr.cs | 2 + src/api/dotnet/ReExpr.cs | 42 +++++++ src/api/dotnet/ReSort.cs | 43 +++++++ src/api/dotnet/SeqExpr.cs | 42 +++++++ src/api/dotnet/SeqSort.cs | 43 +++++++ src/api/dotnet/Sort.cs | 2 + src/smt/theory_seq.cpp | 53 ++++++--- src/smt/theory_seq.h | 3 +- 9 files changed, 454 insertions(+), 20 deletions(-) create mode 100644 src/api/dotnet/ReExpr.cs create mode 100644 src/api/dotnet/ReSort.cs create mode 100644 src/api/dotnet/SeqExpr.cs create mode 100644 src/api/dotnet/SeqSort.cs diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 2e08d3dab..cc81160fb 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -2286,6 +2286,230 @@ namespace Microsoft.Z3 #endregion + #region Sequence, string and regular expresions + + /// + /// Create the empty sequence. + /// + public SeqExpr MkEmptySeq(Sort s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new SeqExpr(this, Native.Z3_mk_seq_empty(nCtx, s.NativeObject)); + } + + /// + /// Create the singleton sequence. + /// + public SeqExpr MkUnit(Expr elem) + { + Contract.Requires(elem != null); + Contract.Ensures(Contract.Result() != null); + return new SeqExpr(this, Native.Z3_mk_seq_unit(nCtx, elem.NativeObject)); + } + + /// + /// Create a string constant. + /// + public SeqExpr MkString(string s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new SeqExpr(this, Native.Z3_mk_string(nCtx, s)); + } + + /// + /// Concatentate sequences. + /// + public SeqExpr MkConcat(params SeqExpr[] t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return new SeqExpr(this, Native.Z3_mk_seq_concat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + + /// + /// Retrieve the length of a given sequence. + /// + public IntExpr MkLength(SeqExpr s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return (IntExpr) Expr.Create(this, Native.Z3_mk_seq_length(nCtx, s.NativeObject)); + } + + /// + /// Check for sequence prefix. + /// + public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) + { + Contract.Requires(s1 != null); + Contract.Requires(s2 != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s1, s2); + return new BoolExpr(this, Native.Z3_mk_seq_prefix(nCtx, s1.NativeObject, s2.NativeObject)); + } + + /// + /// Check for sequence suffix. + /// + public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) + { + Contract.Requires(s1 != null); + Contract.Requires(s2 != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s1, s2); + return new BoolExpr(this, Native.Z3_mk_seq_suffix(nCtx, s1.NativeObject, s2.NativeObject)); + } + + /// + /// Check for sequence containment of s2 in s1. + /// + public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) + { + Contract.Requires(s1 != null); + Contract.Requires(s2 != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s1, s2); + return new BoolExpr(this, Native.Z3_mk_seq_contains(nCtx, s1.NativeObject, s2.NativeObject)); + } + + /// + /// Retrieve sequence of length one at index. + /// + public SeqExpr MkAt(SeqExpr s, IntExpr index) + { + Contract.Requires(s != null); + Contract.Requires(index != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s, index); + return new SeqExpr(this, Native.Z3_mk_seq_at(nCtx, s.NativeObject, index.NativeObject)); + } + + /// + /// Extract subsequence. + /// + public SeqExpr MkExtract(SeqExpr s, IntExpr offset, IntExpr length) + { + Contract.Requires(s != null); + Contract.Requires(offset != null); + Contract.Requires(length != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s, offset, length); + return new SeqExpr(this, Native.Z3_mk_seq_extract(nCtx, s.NativeObject, offset.NativeObject, length.NativeObject)); + } + + /// + /// Extract index of sub-string starting at offset. + /// + public IntExpr MkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) + { + Contract.Requires(s != null); + Contract.Requires(offset != null); + Contract.Requires(substr != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s, substr, offset); + return new IntExpr(this, Native.Z3_mk_seq_index(nCtx, s.NativeObject, substr.NativeObject, offset.NativeObject)); + } + + /// + /// Replace the first occurrence of src by dst in s. + /// + public SeqExpr MkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) + { + Contract.Requires(s != null); + Contract.Requires(src != null); + Contract.Requires(dst != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s, src, dst); + return new SeqExpr(this, Native.Z3_mk_seq_replace(nCtx, s.NativeObject, src.NativeObject, dst.NativeObject)); + } + + /// + /// Convert a regular expression that accepts sequence s. + /// + public ReExpr MkToRe(SeqExpr s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_seq_to_re(nCtx, s.NativeObject)); + } + + + /// + /// Check for regular expression membership. + /// + public BoolExpr MkInRe(SeqExpr s, ReExpr re) + { + Contract.Requires(s != null); + Contract.Requires(re != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(s, re); + return new BoolExpr(this, Native.Z3_mk_seq_in_re(nCtx, s.NativeObject, re.NativeObject)); + } + + /// + /// Take the Kleene star of a regular expression. + /// + public ReExpr MkStar(ReExpr re) + { + Contract.Requires(re != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_re_star(nCtx, re.NativeObject)); + } + + /// + /// Take the Kleene plus of a regular expression. + /// + public ReExpr MPlus(ReExpr re) + { + Contract.Requires(re != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_re_plus(nCtx, re.NativeObject)); + } + + /// + /// Create the optional regular expression. + /// + public ReExpr MOption(ReExpr re) + { + Contract.Requires(re != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_re_option(nCtx, re.NativeObject)); + } + + /// + /// Create the concatenation of regular languages. + /// + public ReExpr MkConcat(params ReExpr[] t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return new ReExpr(this, Native.Z3_mk_re_concat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + /// + /// Create the union of regular languages. + /// + public ReExpr MkUnion(params ReExpr[] t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return new ReExpr(this, Native.Z3_mk_re_union(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + #endregion + #region Pseudo-Boolean constraints /// @@ -4448,6 +4672,26 @@ namespace Microsoft.Z3 throw new Z3Exception("Context mismatch"); } + [Pure] + internal void CheckContextMatch(Z3Object other1, Z3Object other2) + { + Contract.Requires(other1 != null); + Contract.Requires(other2 != null); + CheckContextMatch(other1); + CheckContextMatch(other2); + } + + [Pure] + internal void CheckContextMatch(Z3Object other1, Z3Object other2, Z3Object other3) + { + Contract.Requires(other1 != null); + Contract.Requires(other2 != null); + Contract.Requires(other3 != null); + CheckContextMatch(other1); + CheckContextMatch(other2); + CheckContextMatch(other3); + } + [Pure] internal void CheckContextMatch(Z3Object[] arr) { diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index 599cbf756..c995a12bd 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -1826,6 +1826,8 @@ namespace Microsoft.Z3 case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPExpr(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMExpr(ctx, obj); case Z3_sort_kind.Z3_FINITE_DOMAIN_SORT: return new FiniteDomainExpr(ctx, obj); + case Z3_sort_kind.Z3_RE_SORT: return new ReExpr(ctx, obj); + case Z3_sort_kind.Z3_SEQ_SORT: return new SeqExpr(ctx, obj); } return new Expr(ctx, obj); diff --git a/src/api/dotnet/ReExpr.cs b/src/api/dotnet/ReExpr.cs new file mode 100644 index 000000000..6a10d535f --- /dev/null +++ b/src/api/dotnet/ReExpr.cs @@ -0,0 +1,42 @@ +/*++ +Copyright () 2016 Microsoft Corporation + +Module Name: + + ReExpr.cs + +Abstract: + + Z3 Managed API: Regular Expressions + +Author: + + Christoph Wintersteiger (cwinter) 2012-11-23 + +Notes: + +--*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// Regular expression expressions + /// + public class ReExpr : Expr + { + #region Internal + /// Constructor for ReExpr + internal ReExpr(Context ctx, IntPtr obj) + : base(ctx, obj) + { + Contract.Requires(ctx != null); + } + #endregion + } +} diff --git a/src/api/dotnet/ReSort.cs b/src/api/dotnet/ReSort.cs new file mode 100644 index 000000000..bc420603d --- /dev/null +++ b/src/api/dotnet/ReSort.cs @@ -0,0 +1,43 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + ReSort.cs + +Abstract: + + Z3 Managed API: Regular expression Sorts + +Author: + + Christoph Wintersteiger (cwinter) 2012-11-23 + +Notes: + +--*/ + +using System; +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// A regular expression sort + /// + public class ReSort : Sort + { + #region Internal + internal ReSort(Context ctx, IntPtr obj) + : base(ctx, obj) + { + Contract.Requires(ctx != null); + } + internal ReSort(Context ctx) + : base(ctx, Native.Z3_mk_int_sort(ctx.nCtx)) + { + Contract.Requires(ctx != null); + } + #endregion + } +} diff --git a/src/api/dotnet/SeqExpr.cs b/src/api/dotnet/SeqExpr.cs new file mode 100644 index 000000000..c9fdd03a8 --- /dev/null +++ b/src/api/dotnet/SeqExpr.cs @@ -0,0 +1,42 @@ +/*++ +Copyright () 2016 Microsoft Corporation + +Module Name: + + SeqExpr.cs + +Abstract: + + Z3 Managed API: Sequence Expressions + +Author: + + Christoph Wintersteiger (cwinter) 2012-11-23 + +Notes: + +--*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// Sequence expressions + /// + public class SeqExpr : Expr + { + #region Internal + /// Constructor for SeqExpr + internal SeqExpr(Context ctx, IntPtr obj) + : base(ctx, obj) + { + Contract.Requires(ctx != null); + } + #endregion + } +} diff --git a/src/api/dotnet/SeqSort.cs b/src/api/dotnet/SeqSort.cs new file mode 100644 index 000000000..b2be11291 --- /dev/null +++ b/src/api/dotnet/SeqSort.cs @@ -0,0 +1,43 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + SeqSort.cs + +Abstract: + + Z3 Managed API: Sequence Sorts + +Author: + + Christoph Wintersteiger (cwinter) 2012-11-23 + +Notes: + +--*/ + +using System; +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// A Sequence sort + /// + public class SeqSort : Sort + { + #region Internal + internal SeqSort(Context ctx, IntPtr obj) + : base(ctx, obj) + { + Contract.Requires(ctx != null); + } + internal SeqSort(Context ctx) + : base(ctx, Native.Z3_mk_int_sort(ctx.nCtx)) + { + Contract.Requires(ctx != null); + } + #endregion + } +} diff --git a/src/api/dotnet/Sort.cs b/src/api/dotnet/Sort.cs index 412398ddd..e1b8ca1b7 100644 --- a/src/api/dotnet/Sort.cs +++ b/src/api/dotnet/Sort.cs @@ -147,6 +147,8 @@ namespace Microsoft.Z3 case Z3_sort_kind.Z3_RELATION_SORT: return new RelationSort(ctx, obj); case Z3_sort_kind.Z3_FLOATING_POINT_SORT: return new FPSort(ctx, obj); case Z3_sort_kind.Z3_ROUNDING_MODE_SORT: return new FPRMSort(ctx, obj); + case Z3_sort_kind.Z3_SEQ_SORT: return new SeqSort(ctx, obj); + case Z3_sort_kind.Z3_RE_SORT: return new ReSort(ctx, obj); default: throw new Z3Exception("Unknown sort kind"); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 88988d2b4..1d640fe78 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -209,7 +209,7 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>branch_variable\n";); return FC_CONTINUE; } - if (!check_length_coherence()) { + if (check_length_coherence()) { ++m_stats.m_check_length_coherence; TRACE("seq", tout << ">>check_length_coherence\n";); return FC_CONTINUE; @@ -374,29 +374,36 @@ bool theory_seq::propagate_length_coherence(expr* e) { expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); add_axiom(~mk_literal(high1), mk_literal(high2)); } - m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); return true; } +bool theory_seq::check_length_coherence(expr* e) { + if (is_var(e) && m_rep.is_root(e)) { + expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); + expr_ref head(m), tail(m); + if (!propagate_length_coherence(e) && + l_false == assume_equality(e, emp)) { + // e = emp \/ e = unit(head.elem(e))*tail(e) + mk_decompose(e, head, tail); + expr_ref conc(m_util.str.mk_concat(head, tail), m); + propagate_is_conc(e, conc); + assume_equality(tail, emp); + } + m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); + return true; + } + return false; +} + bool theory_seq::check_length_coherence() { obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); for (; it != end; ++it) { expr* e = *it; - if (is_var(e) && m_rep.is_root(e)) { - expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); - expr_ref head(m), tail(m); - if (!propagate_length_coherence(e) && - l_false == assume_equality(e, emp)) { - // e = emp \/ e = unit(head.elem(e))*tail(e) - mk_decompose(e, head, tail); - expr_ref conc(m_util.str.mk_concat(head, tail), m); - propagate_is_conc(e, conc); - assume_equality(tail, emp); - } - return false; + if (check_length_coherence(e)) { + return true; } } - return true; + return false; } /* @@ -936,6 +943,12 @@ bool theory_seq::simplify_and_solve_eqs() { bool theory_seq::internalize_term(app* term) { TRACE("seq", tout << mk_pp(term, m) << "\n";); context & ctx = get_context(); + if (ctx.e_internalized(term)) { + enode* e = ctx.get_enode(term); + mk_var(e); + return true; + } + unsigned num_args = term->get_num_args(); expr* arg; for (unsigned i = 0; i < num_args; i++) { @@ -947,6 +960,7 @@ bool theory_seq::internalize_term(app* term) { ctx.set_var_theory(bv, get_id()); ctx.mark_as_relevant(bv); } + enode* e = 0; if (ctx.e_internalized(term)) { e = ctx.get_enode(term); @@ -1049,7 +1063,9 @@ void theory_seq::display_disequation(std::ostream& out, ne const& e) const { for (unsigned j = 0; j < e.m_lhs.size(); ++j) { out << mk_pp(e.m_lhs[j], m) << " != " << mk_pp(e.m_rhs[j], m) << "\n"; } - display_deps(out, e.m_dep); + if (e.m_dep) { + display_deps(out, e.m_dep); + } } void theory_seq::display_deps(std::ostream& out, dependency* dep) const { @@ -1063,8 +1079,7 @@ void theory_seq::display_deps(std::ostream& out, dependency* dep) const { literal lit = lits[i]; get_context().display_literals_verbose(out << "\n ", 1, &lit); } - out << "\n"; - + out << "\n"; } void theory_seq::collect_statistics(::statistics & st) const { @@ -1287,7 +1302,7 @@ expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { m_rep.add_cache(e0, edr); eqs = m_dm.mk_join(eqs, deps); TRACE("seq_verbose", tout << mk_pp(e0, m) << " |--> " << result << "\n"; - display_deps(tout, eqs);); + if (eqs) display_deps(tout, eqs);); return result; } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index db5fa0f8b..40fce2695 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -257,7 +257,7 @@ namespace smt { public: replay_length_coherence(ast_manager& m, expr* e) : m_e(e, m) {} virtual void operator()(theory_seq& th) { - th.propagate_length_coherence(m_e); + th.check_length_coherence(m_e); } }; @@ -348,6 +348,7 @@ namespace smt { bool split_variable(); // split a variable bool is_solved(); bool check_length_coherence(); + bool check_length_coherence(expr* e); bool propagate_length_coherence(expr* e); bool solve_eqs(unsigned start); From c1ebf6b4fca2e980ef1c44cfafe3fb4f4f839a9c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Jan 2016 18:01:48 -0800 Subject: [PATCH 31/35] seq + API Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/Context.cs | 37 ++++++ src/api/java/Context.java | 191 ++++++++++++++++++++++++++++++ src/api/java/Expr.java | 4 + src/api/java/Sort.java | 4 + src/api/python/z3.py | 28 ++++- src/ast/rewriter/seq_rewriter.cpp | 150 ++++++++++++++--------- src/ast/rewriter/seq_rewriter.h | 2 + src/smt/theory_seq.cpp | 132 ++++++++++++++++++--- src/smt/theory_seq.h | 13 ++ 9 files changed, 484 insertions(+), 77 deletions(-) diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index cc81160fb..a97036897 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -125,6 +125,7 @@ namespace Microsoft.Z3 private BoolSort m_boolSort = null; private IntSort m_intSort = null; private RealSort m_realSort = null; + private SeqSort m_stringSort = null; /// /// Retrieves the Boolean sort of the context. @@ -163,6 +164,20 @@ namespace Microsoft.Z3 } } + /// + /// Retrieves the String sort of the context. + /// + public SeqSort StringSort + { + get + { + Contract.Ensures(Contract.Result() != null); + if (m_stringSort == null) m_stringSort = new SeqSort(this, Native.Z3_mk_string_sort(nCtx)); + return m_stringSort; + } + } + + /// /// Create a new Boolean sort. /// @@ -223,6 +238,27 @@ namespace Microsoft.Z3 return new BitVecSort(this, Native.Z3_mk_bv_sort(nCtx, size)); } + + /// + /// Create a new sequence sort. + /// + public SeqSort MkSeqSort(Sort s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new SeqSort(this, Native.Z3_mk_seq_sort(nCtx, s.NativeObject)); + } + + /// + /// Create a new regular expression sort. + /// + public ReSort MkReSort(SeqSort s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new ReSort(this, Native.Z3_mk_re_sort(nCtx, s.NativeObject)); + } + /// /// Create a new array sort. /// @@ -4872,6 +4908,7 @@ namespace Microsoft.Z3 m_boolSort = null; m_intSort = null; m_realSort = null; + m_stringSort = null; } #endregion } diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 8d2bc5b50..f27402e91 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -1849,6 +1849,184 @@ public class Context extends IDisposable arg2.getNativeObject())); } + + /** + * Sequences, Strings and regular expressions. + */ + + /** + * Create the empty sequence. + */ + public SeqExpr MkEmptySeq(Sort s) + { + checkContextMatch(s); + return new SeqExpr(this, Native.mkSeqEmpty(nCtx, s.NativeObject)); + } + + /** + * Create the singleton sequence. + */ + public SeqExpr MkUnit(Expr elem) + { + checkContextMatch(elem); + return new SeqExpr(this, Native.mkSeqUnit(nCtx, elem.NativeObject)); + } + + /** + * Create a string constant. + */ + public SeqExpr MkString(string s) + { + return new SeqExpr(this, Native.mkString(nCtx, s)); + } + + /** + * Concatentate sequences. + */ + public SeqExpr MkConcat(params SeqExpr[] t) + { + checkContextMatch(t); + return new SeqExpr(this, Native.mkSeqConcat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + + /** + * Retrieve the length of a given sequence. + */ + public IntExpr MkLength(SeqExpr s) + { + checkContextMatch(s); + return (IntExpr) Expr.Create(this, Native.mkSeqLength(nCtx, s.NativeObject)); + } + + /** + * Check for sequence prefix. + */ + public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) + { + checkContextMatch(s1, s2); + return new BoolExpr(this, Native.mkSeqPrefix(nCtx, s1.NativeObject, s2.NativeObject)); + } + + /** + * Check for sequence suffix. + */ + public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) + { + checkContextMatch(s1, s2); + return new BoolExpr(this, Native.mkSeqSuffix(nCtx, s1.NativeObject, s2.NativeObject)); + } + + /** + * Check for sequence containment of s2 in s1. + */ + public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) + { + checkContextMatch(s1, s2); + return new BoolExpr(this, Native.mkSeqContains(nCtx, s1.NativeObject, s2.NativeObject)); + } + + /** + * Retrieve sequence of length one at index. + */ + public SeqExpr MkAt(SeqExpr s, IntExpr index) + { + checkContextMatch(s, index); + return new SeqExpr(this, Native.mkSeqAt(nCtx, s.NativeObject, index.NativeObject)); + } + + /** + * Extract subsequence. + */ + public SeqExpr MkExtract(SeqExpr s, IntExpr offset, IntExpr length) + { + checkContextMatch(s, offset, length); + return new SeqExpr(this, Native.mkSeqExtract(nCtx, s.NativeObject, offset.NativeObject, length.NativeObject)); + } + + /** + * Extract index of sub-string starting at offset. + */ + public IntExpr MkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) + { + checkContextMatch(s, substr, offset); + return new IntExpr(this, Native.mkSeqIndex(nCtx, s.NativeObject, substr.NativeObject, offset.NativeObject)); + } + + /** + * Replace the first occurrence of src by dst in s. + */ + public SeqExpr MkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) + { + checkContextMatch(s, src, dst); + return new SeqExpr(this, Native.mkSeqReplace(nCtx, s.NativeObject, src.NativeObject, dst.NativeObject)); + } + + /** + * Convert a regular expression that accepts sequence s. + */ + public ReExpr MkToRe(SeqExpr s) + { + checkContextMatch(s); + return new ReExpr(this, Native.mkSeqToRe(nCtx, s.NativeObject)); + } + + + /** + * Check for regular expression membership. + */ + public BoolExpr MkInRe(SeqExpr s, ReExpr re) + { + checkContextMatch(s, re); + return new BoolExpr(this, Native.mkSeqInRe(nCtx, s.NativeObject, re.NativeObject)); + } + + /** + * Take the Kleene star of a regular expression. + */ + public ReExpr MkStar(ReExpr re) + { + checkContextMatch(re); + return new ReExpr(this, Native.mkReStar(nCtx, re.NativeObject)); + } + + /** + * Take the Kleene plus of a regular expression. + */ + public ReExpr MPlus(ReExpr re) + { + checkContextMatch(re); + return new ReExpr(this, Native.mkRePlus(nCtx, re.NativeObject)); + } + + /** + * Create the optional regular expression. + */ + public ReExpr MOption(ReExpr re) + { + checkContextMatch(re); + return new ReExpr(this, Native.mkReOption(nCtx, re.NativeObject)); + } + + /** + * Create the concatenation of regular languages. + */ + public ReExpr MkConcat(ReExpr[] t) + { + checkContextMatch(t); + return new ReExpr(this, Native.mkReConcat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + /** + * Create the union of regular languages. + */ + public ReExpr MkUnion(ReExpr[] t) + { + checkContextMatch(t); + return new ReExpr(this, Native.mkReUnion(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + /** * Create a Term of a given sort. * @param v A string representing the term value in decimal notation. If the given sort is a real, then the @@ -3683,6 +3861,19 @@ public class Context extends IDisposable throw new Z3Exception("Context mismatch"); } + void checkContextMatch(Z3Object other1, Z3Object other2) + { + checkContextMatch(other1); + checkContextMatch(other2); + } + + void checkContextMatch(Z3Object other1, Z3Object other2, Z3Object other3) + { + checkContextMatch(other1); + checkContextMatch(other2); + checkContextMatch(other3); + } + void checkContextMatch(Z3Object[] arr) { if (arr != null) diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index b0b95d4b7..e03d5b1c9 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -2186,6 +2186,10 @@ public class Expr extends AST return new FPRMExpr(ctx, obj); case Z3_FINITE_DOMAIN_SORT: return new FiniteDomainExpr(ctx, obj); + case Z3_SEQ_SORT: + return new SeqExpr(ctx, obj); + case Z3_RE_SORT: + return new ReExpr(ctx, obj); default: ; } diff --git a/src/api/java/Sort.java b/src/api/java/Sort.java index db6bee80e..1481a44e2 100644 --- a/src/api/java/Sort.java +++ b/src/api/java/Sort.java @@ -141,6 +141,10 @@ public class Sort extends AST return new FPSort(ctx, obj); case Z3_ROUNDING_MODE_SORT: return new FPRMSort(ctx, obj); + case Z3_SEQ_SORT: + return new SeqSort(ctx, obj); + case Z3_RE_SORT: + return new ReSort(ctx, obj); default: throw new Z3Exception("Unknown sort kind"); } diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 0bef54a53..9129cb854 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -8991,7 +8991,7 @@ class SeqRef(ExprRef): def __getitem__(self, i): return SeqRef(Z3_mk_seq_at(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) - def is_string_sort(self): + def is_string(self): return Z3_is_string_sort(self.ctx_ref(), Z3_get_sort(self.ctx_ref(), self.as_ast())) def is_string_value(self): @@ -9026,12 +9026,12 @@ def is_seq(a): """ return isinstance(a, SeqRef) -def is_string_sort(a): +def is_string(a): """Return `True` if `a` is a Z3 string expression. - >>> print (is_string_sort(StringVal("ab"))) + >>> print (is_string(StringVal("ab"))) True """ - return isinstance(a, SeqRef) and a.is_string_sort() + return isinstance(a, SeqRef) and a.is_string() def is_string_value(a): """return 'True' if 'a' is a Z3 string constant expression. @@ -9042,11 +9042,27 @@ def is_string_value(a): """ return isinstance(a, SeqRef) and a.is_string_value() + def StringVal(s, ctx=None): """create a string expression""" ctx = _get_ctx(ctx) return SeqRef(Z3_mk_string(ctx.ref(), s), ctx) +def String(name, ctx=None): + """Return a string constant named `name`. If `ctx=None`, then the global context is used. + + >>> x = String('x') + """ + ctx = _get_ctx(ctx) + return SeqRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), StringSort(ctx).ast), ctx) + +def Strings(names, ctx=None): + """Return a tuple of String constants. """ + ctx = _get_ctx(ctx) + if isinstance(names, str): + names = names.split(" ") + return [String(name, ctx) for name in names] + def Empty(s): """Create the empty sequence of the given sort >>> e = Empty(StringSort()) @@ -9101,6 +9117,10 @@ def Contains(a, b): >>> s2 = Contains("abc", "bc") >>> simplify(s2) True + >>> x, y, z = Strings('x y z') + >>> s3 = Contains(Concat(x,y,z), y) + >>> simplify(s3) + True """ ctx = _get_ctx2(a, b) a = _coerce_seq(a, ctx) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 8f6aa6fca..1231369c7 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -23,6 +23,7 @@ Notes: #include"ast_util.h" #include"uint_set.h" #include"automaton.h" +#include"well_sorted.h" struct display_expr1 { @@ -843,37 +844,33 @@ br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) { return BR_REWRITE3; } -bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_vector& rhs) { +bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_vector& lhs, expr_ref_vector& rhs) { expr* a, *b; zstring s; bool change = false; - m_lhs.reset(); - m_rhs.reset(); - m_util.str.get_concat(l, m_lhs); - m_util.str.get_concat(r, m_rhs); // solve from back while (true) { - while (!m_rhs.empty() && m_util.str.is_empty(m_rhs.back())) { - m_rhs.pop_back(); + while (!rs.empty() && m_util.str.is_empty(rs.back())) { + rs.pop_back(); change = true; } - while (!m_lhs.empty() && m_util.str.is_empty(m_lhs.back())) { - m_lhs.pop_back(); + while (!ls.empty() && m_util.str.is_empty(ls.back())) { + ls.pop_back(); change = true; } - if (m_lhs.empty() || m_rhs.empty()) { + if (ls.empty() || rs.empty()) { break; } - expr* l = m_lhs.back(); - expr* r = m_rhs.back(); + expr* l = ls.back(); + expr* r = rs.back(); if (m_util.str.is_unit(r) && m_util.str.is_string(l)) { std::swap(l, r); - m_lhs.swap(m_rhs); + ls.swap(rs); } if (l == r) { - m_lhs.pop_back(); - m_rhs.pop_back(); + ls.pop_back(); + rs.pop_back(); } else if(m_util.str.is_unit(l, a) && m_util.str.is_unit(r, b)) { @@ -882,8 +879,8 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve } lhs.push_back(a); rhs.push_back(b); - m_lhs.pop_back(); - m_rhs.pop_back(); + ls.pop_back(); + rs.pop_back(); } else if (m_util.str.is_unit(l, a) && m_util.str.is_string(r, s)) { SASSERT(s.length() > 0); @@ -892,13 +889,13 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve SASSERT(m().get_sort(ch) == m().get_sort(a)); lhs.push_back(ch); rhs.push_back(a); - m_lhs.pop_back(); + ls.pop_back(); if (s.length() == 1) { - m_rhs.pop_back(); + rs.pop_back(); } else { expr_ref s2(m_util.str.mk_string(s.extract(0, s.length()-2)), m()); - m_rhs[m_rhs.size()-1] = s2; + rs[rs.size()-1] = s2; } } else { @@ -910,22 +907,22 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve // solve from front unsigned head1 = 0, head2 = 0; while (true) { - while (head1 < m_lhs.size() && m_util.str.is_empty(m_lhs[head1].get())) { + while (head1 < ls.size() && m_util.str.is_empty(ls[head1].get())) { ++head1; } - while (head2 < m_rhs.size() && m_util.str.is_empty(m_rhs[head2].get())) { + while (head2 < rs.size() && m_util.str.is_empty(rs[head2].get())) { ++head2; } - if (head1 == m_lhs.size() || head2 == m_rhs.size()) { + if (head1 == ls.size() || head2 == rs.size()) { break; } - SASSERT(head1 < m_lhs.size() && head2 < m_rhs.size()); + SASSERT(head1 < ls.size() && head2 < rs.size()); - expr* l = m_lhs[head1].get(); - expr* r = m_rhs[head2].get(); + expr* l = ls[head1].get(); + expr* r = rs[head2].get(); if (m_util.str.is_unit(r) && m_util.str.is_string(l)) { std::swap(l, r); - m_lhs.swap(m_rhs); + ls.swap(rs); } if (l == r) { ++head1; @@ -947,13 +944,13 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve SASSERT(m().get_sort(ch) == m().get_sort(a)); lhs.push_back(ch); rhs.push_back(a); - m_lhs.pop_back(); + ls.pop_back(); if (s.length() == 1) { - m_rhs.pop_back(); + rs.pop_back(); } else { expr_ref s2(m_util.str.mk_string(s.extract(1, s.length()-1)), m()); - m_rhs[m_rhs.size()-1] = s2; + rs[rs.size()-1] = s2; } } else { @@ -963,10 +960,10 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve } // reduce strings zstring s1, s2; - while (head1 < m_lhs.size() && - head2 < m_rhs.size() && - m_util.str.is_string(m_lhs[head1].get(), s1) && - m_util.str.is_string(m_rhs[head2].get(), s2)) { + while (head1 < ls.size() && + head2 < rs.size() && + m_util.str.is_string(ls[head1].get(), s1) && + m_util.str.is_string(rs[head2].get(), s2)) { unsigned l = std::min(s1.length(), s2.length()); for (unsigned i = 0; i < l; ++i) { if (s1[i] != s2[i]) { @@ -977,64 +974,105 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve ++head1; } else { - m_lhs[head1] = m_util.str.mk_string(s1.extract(l, s1.length()-l)); + ls[head1] = m_util.str.mk_string(s1.extract(l, s1.length()-l)); } if (l == s2.length()) { ++head2; } else { - m_rhs[head2] = m_util.str.mk_string(s2.extract(l, s2.length()-l)); + rs[head2] = m_util.str.mk_string(s2.extract(l, s2.length()-l)); } change = true; } - while (head1 < m_lhs.size() && - head2 < m_rhs.size() && - m_util.str.is_string(m_lhs.back(), s1) && - m_util.str.is_string(m_rhs.back(), s2)) { + while (head1 < ls.size() && + head2 < rs.size() && + m_util.str.is_string(ls.back(), s1) && + m_util.str.is_string(rs.back(), s2)) { unsigned l = std::min(s1.length(), s2.length()); for (unsigned i = 0; i < l; ++i) { if (s1[s1.length()-i-1] != s2[s2.length()-i-1]) { return false; } } - m_lhs.pop_back(); - m_rhs.pop_back(); + ls.pop_back(); + rs.pop_back(); if (l < s1.length()) { - m_lhs.push_back(m_util.str.mk_string(s1.extract(0, s1.length()-l))); + ls.push_back(m_util.str.mk_string(s1.extract(0, s1.length()-l))); } if (l < s2.length()) { - m_rhs.push_back(m_util.str.mk_string(s2.extract(0, s2.length()-l))); + rs.push_back(m_util.str.mk_string(s2.extract(0, s2.length()-l))); } change = true; } bool is_sat; - unsigned szl = m_lhs.size() - head1, szr = m_rhs.size() - head2; - expr* const* ls = m_lhs.c_ptr() + head1, * const*rs = m_rhs.c_ptr() + head2; - if (length_constrained(szl, ls, szr, rs, lhs, rhs, is_sat)) { + unsigned szl = ls.size() - head1, szr = rs.size() - head2; + expr* const* _ls = ls.c_ptr() + head1, * const* _rs = rs.c_ptr() + head2; + if (length_constrained(szl, _ls, szr, _rs, lhs, rhs, is_sat)) { + ls.reset(); rs.reset(); return is_sat; } - if (is_subsequence(szl, ls, szr, rs, lhs, rhs, is_sat)) { + if (is_subsequence(szl, _ls, szr, _rs, lhs, rhs, is_sat)) { + ls.reset(); rs.reset(); return is_sat; } - if (szl == 0 && szr == 0) { - return true; - } + if (szl == 0 && szr == 0) { + ls.reset(); rs.reset(); + return true; + } else if (!change) { - lhs.push_back(l); - rhs.push_back(r); + // skip } else { // could solve if either side is fixed size. SASSERT(szl > 0 && szr > 0); - - lhs.push_back(m_util.str.mk_concat(szl, ls)); - rhs.push_back(m_util.str.mk_concat(szr, rs)); + if (head1 > 0) { + for (unsigned i = 0; i < szl; ++i) { + ls[i] = ls[i + head1]; + } + } + ls.shrink(szl); + if (head2 > 0) { + for (unsigned i = 0; i < szr; ++i) { + rs[i] = rs[i + head2]; + } + } + rs.shrink(szr); + lhs.push_back(m_util.str.mk_concat(ls.size(), ls.c_ptr())); + rhs.push_back(m_util.str.mk_concat(rs.size(), rs.c_ptr())); + ls.reset(); + rs.reset(); } return true; } +bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_vector& rhs) { + m_lhs.reset(); + m_rhs.reset(); + m_util.str.get_concat(l, m_lhs); + m_util.str.get_concat(r, m_rhs); + if (reduce_eq(m_lhs, m_rhs, lhs, rhs)) { + SASSERT(lhs.size() == rhs.size()); + if (!m_lhs.empty()) { + SASSERT(!m_rhs.empty()); + lhs.push_back(m_util.str.mk_concat(m_lhs.size(), m_lhs.c_ptr())); + rhs.push_back(m_util.str.mk_concat(m_rhs.size(), m_rhs.c_ptr())); + } + for (unsigned i = 0; i < lhs.size(); ++i) { + SASSERT(is_well_sorted(m(), lhs[i].get())); + SASSERT(is_well_sorted(m(), rhs[i].get())); + SASSERT(m().get_sort(lhs[i].get()) == m().get_sort(rhs[i].get())); + TRACE("seq", tout << mk_pp(lhs[i].get(), m()) << " = " << mk_pp(rhs[i].get(), m()) << "\n";); + } + return true; + } + else { + TRACE("seq", tout << mk_pp(l, m()) << " != " << mk_pp(r, m()) << "\n";); + return false; + } +} + expr* seq_rewriter::concat_non_empty(unsigned n, expr* const* as) { SASSERT(n > 0); ptr_vector bs; diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index d5fac104b..9d4dbb14f 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -92,6 +92,8 @@ public: bool reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_vector& rhs); + bool reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_vector& lhs, expr_ref_vector& rhs); + }; #endif diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 1d640fe78..7aa434661 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -368,11 +368,19 @@ bool theory_seq::propagate_length_coherence(expr* e) { // len(e) >= low => e = tail; literal low(mk_literal(m_autil.mk_ge(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true)))); add_axiom(~low, mk_eq(e, tail, false)); - assume_equality(seq, emp); if (upper_bound(e, hi)) { - expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); - expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); - add_axiom(~mk_literal(high1), mk_literal(high2)); + // len(e) <= hi => len(tail) <= hi - lo + expr_ref high1(m_autil.mk_le(m_util.str.mk_length(e), m_autil.mk_numeral(hi, true)), m); + if (hi == lo) { + add_axiom(~mk_literal(high1), mk_eq(seq, emp, false)); + } + else { + expr_ref high2(m_autil.mk_le(m_util.str.mk_length(seq), m_autil.mk_numeral(hi-lo, true)), m); + add_axiom(~mk_literal(high1), mk_literal(high2)); + } + } + else { + assume_equality(seq, emp); } return true; } @@ -428,6 +436,15 @@ bool theory_seq::is_nth(expr* e) const { return is_skolem(m_nth, e); } +bool theory_seq::is_tail(expr* e, expr*& s, unsigned& idx) const { + rational r; + return + is_skolem(m_tail, e) && + m_autil.is_numeral(to_app(e)->get_arg(1), r) && + (idx = r.get_unsigned(), s = to_app(e)->get_arg(0), true); +} + + expr_ref theory_seq::mk_nth(expr* s, expr* idx) { sort* char_sort = 0; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); @@ -602,9 +619,9 @@ bool theory_seq::simplify_eq(expr* l, expr* r, dependency* deps) { } } TRACE("seq", - tout << mk_pp(l, m) << " = " << mk_pp(r, m) << " => "; + tout << mk_pp(l, m) << " = " << mk_pp(r, m) << " => \n"; for (unsigned i = 0; i < lhs.size(); ++i) { - tout << mk_pp(lhs[i].get(), m) << " = " << mk_pp(rhs[i].get(), m) << "; "; + tout << mk_pp(lhs[i].get(), m) << "\n = \n" << mk_pp(rhs[i].get(), m) << "; \n"; } tout << "\n";); return true; @@ -614,6 +631,8 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { if (l == r) { return true; } + //propagate_max_length(l, r, deps); + if (is_var(l) && !occurs(l, r) && add_solution(l, r, deps)) { return true; } @@ -712,6 +731,21 @@ bool theory_seq::solve_eq(expr* _l, expr* _r, dependency* deps) { return false; } +bool theory_seq::propagate_max_length(expr* l, expr* r, dependency* deps) { + unsigned idx; + expr* s; + if (m_util.str.is_empty(l)) { + std::swap(l, r); + } + rational hi; + if (is_tail(l, s, idx) && has_length(s) && m_util.str.is_empty(r) && !upper_bound(s, hi)) { + std::cout << "max length " << mk_pp(s, m) << " " << idx << "\n"; + propagate_lit(deps, 0, 0, mk_literal(m_autil.mk_le(m_util.str.mk_length(s), m_autil.mk_int(idx+1)))); + return true; + } + return false; +} + bool theory_seq::is_binary_eq(expr* l, expr* r, expr*& x, ptr_vector& xs, ptr_vector& ys, expr*& y) { xs.reset(); ys.reset(); @@ -889,13 +923,35 @@ void theory_seq::solve_ne(unsigned idx) { --i; } } - if (num_undef_lits == 0 && n.m_lhs.empty()) { + if (num_undef_lits == 1 && n.m_lhs.empty()) { + literal_vector lits; + literal undef_lit = null_literal; + for (unsigned i = 0; i < n.m_lits.size(); ++i) { + literal lit = n.m_lits[i]; + switch (ctx.get_assignment(lit)) { + case l_true: + lits.push_back(lit); + break; + case l_false: + UNREACHABLE(); + break; + case l_undef: + SASSERT(undef_lit == null_literal); + undef_lit = lit; + break; + } + } + TRACE("seq", tout << "propagate: " << undef_lit << "\n";); + SASSERT(undef_lit != null_literal); + propagate_lit(n.m_dep, lits.size(), lits.c_ptr(), ~undef_lit); + } + else if (num_undef_lits == 0 && n.m_lhs.empty()) { literal_vector lits(n.m_lits); lits.push_back(~mk_eq(n.m_l, n.m_r, false)); set_conflict(n.m_dep, lits); SASSERT(m_new_propagation); } - if (num_undef_lits == 0 && n.m_lhs.size() == 1) { + else if (false && num_undef_lits == 0 && n.m_lhs.size() == 1) { expr* l = n.m_lhs[0]; expr* r = n.m_rhs[0]; if (m_util.str.is_empty(r)) { @@ -903,13 +959,21 @@ void theory_seq::solve_ne(unsigned idx) { } if (m_util.str.is_empty(l) && is_var(r)) { literal lit = ~mk_eq_empty(r); - if (ctx.get_assignment(lit) == l_true) { + switch (ctx.get_assignment(lit)) { + case l_true: { expr_ref head(m), tail(m); mk_decompose(r, head, tail); expr_ref conc(m_util.str.mk_concat(head, tail), m); propagate_is_conc(r, conc); + m_new_propagation = true; + break; + } + case l_undef: + m_new_propagation = true; + break; + case l_false: + break; } - m_new_propagation = true; } } } @@ -941,14 +1005,13 @@ bool theory_seq::simplify_and_solve_eqs() { bool theory_seq::internalize_term(app* term) { - TRACE("seq", tout << mk_pp(term, m) << "\n";); context & ctx = get_context(); if (ctx.e_internalized(term)) { enode* e = ctx.get_enode(term); mk_var(e); return true; } - + TRACE("seq", tout << mk_pp(term, m) << "\n";); unsigned num_args = term->get_num_args(); expr* arg; for (unsigned i = 0; i < num_args; i++) { @@ -1090,7 +1153,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq branch", m_stats.m_branch_variable); st.update("seq solve !=", m_stats.m_solve_nqs); st.update("seq solve =", m_stats.m_solve_eqs); - + st.update("seq add axiom", m_stats.m_add_axiom); } void theory_seq::init_model(model_generator & mg) { @@ -1266,6 +1329,29 @@ expr_ref theory_seq::canonize(expr* e, dependency*& eqs) { return result; } +void theory_seq::canonize(expr* e0, expr_ref_vector& es, dependency*& eqs) { + dependency* dep = 0; + expr* e = m_rep.find(e0, dep); + expr* e1, *e2; + if (m_util.str.is_concat(e, e1, e2)) { + canonize(e1, es, eqs); + canonize(e2, es, eqs); + } + else if (m_util.str.is_empty(e)) { + // skip + } + else { + expr_ref e3 = expand(e, eqs); + if (m_util.str.is_concat(e3) || m_util.str.is_empty(e3)) { + canonize(e3, es, eqs); + } + else { + es.push_back(e3); + } + } + eqs = m_dm.mk_join(eqs, dep); +} + expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { expr_ref result(m); dependency* deps = 0; @@ -1516,10 +1602,12 @@ void theory_seq::add_length_axiom(expr* n) { if (n != len) { TRACE("seq", tout << "Add length coherence for " << mk_pp(n, m) << "\n";); add_axiom(mk_eq(n, len, false)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, n))); } } else { add_axiom(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, n))); } } @@ -1753,7 +1841,14 @@ void theory_seq::propagate_step(literal lit, expr* step) { expr_ref nth = mk_nth(s, idx); TRACE("seq", tout << mk_pp(step, m) << " -> " << mk_pp(t, m) << " = " << nth << "\n";); propagate_eq(lit, t, nth); - propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); + rational lo; + rational _idx; + if (lower_bound(s, lo) && lo.is_unsigned() && m_autil.is_numeral(idx, _idx) && lo >= _idx) { + // skip + } + else { + propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); + } ensure_nth(lit, s, idx); } @@ -1774,7 +1869,7 @@ void theory_seq::ensure_nth(literal lit, expr* s, expr* idx) { expr_ref_vector elems(m); get_concat(s1, es); unsigned i = 0; - for (; i < _idx && i < es.size() && m_util.str.is_unit(es[i]); ++i) {}; + for (; i <= _idx && i < es.size() && m_util.str.is_unit(es[i]); ++i) {}; if (i == _idx && i < es.size() && m_util.str.is_unit(es[i], e1)) { dep = m_dm.mk_join(dep, m_dm.mk_leaf(assumption(lit))); propagate_eq(dep, ensure_enode(nth), ensure_enode(e1)); @@ -1792,7 +1887,9 @@ void theory_seq::ensure_nth(literal lit, expr* s, expr* idx) { propagate_eq(lit, s, conc, true); // TBD: examine other places for enforcing constraints on tail - add_axiom(~lit, mk_eq(m_util.str.mk_length(s), m_util.str.mk_length(conc), false)); + conc = m_autil.mk_add(m_autil.mk_int(_idx+1), m_util.str.mk_length(s2)); + add_axiom(~lit, mk_eq(m_util.str.mk_length(s), conc, false)); + //add_axiom(~lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), m_autil.mk_int(_idx + 1)))); } literal theory_seq::mk_literal(expr* _e) { @@ -1823,8 +1920,9 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4) { if (l2 != null_literal) { ctx.mark_as_relevant(l2); lits.push_back(l2); } if (l3 != null_literal) { ctx.mark_as_relevant(l3); lits.push_back(l3); } if (l4 != null_literal) { ctx.mark_as_relevant(l4); lits.push_back(l4); } - TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + TRACE("seq", ctx.display_literals_verbose(tout << "axiom: ", lits.size(), lits.c_ptr()); tout << "\n";); m_new_propagation = true; + ++m_stats.m_add_axiom; ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 40fce2695..511c6917c 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -261,6 +261,15 @@ namespace smt { } }; + class replay_axiom : public apply { + expr_ref m_e; + public: + replay_axiom(ast_manager& m, expr* e) : m_e(e, m) {} + virtual void operator()(theory_seq& th) { + th.enque_axiom(m_e); + } + }; + class push_replay : public trail { apply* m_apply; public: @@ -282,6 +291,7 @@ namespace smt { unsigned m_branch_variable; unsigned m_solve_nqs; unsigned m_solve_eqs; + unsigned m_add_axiom; }; ast_manager& m; dependency_manager m_dm; @@ -357,6 +367,7 @@ namespace smt { bool solve_unit_eq(expr* l, expr* r, dependency* dep); bool is_binary_eq(expr* l, expr* r, expr*& x, ptr_vector& xunits, ptr_vector& yunits, expr*& y); bool solve_binary_eq(expr* l, expr* r, dependency* dep); + bool propagate_max_length(expr* l, expr* r, dependency* dep); bool solve_nqs(unsigned i); void solve_ne(unsigned i); @@ -383,9 +394,11 @@ namespace smt { bool is_var(expr* b); bool add_solution(expr* l, expr* r, dependency* dep); bool is_nth(expr* a) const; + bool is_tail(expr* a, expr*& s, unsigned& idx) const; expr_ref mk_nth(expr* s, expr* idx); expr_ref mk_last(expr* e); expr_ref canonize(expr* e, dependency*& eqs); + void canonize(expr* e, expr_ref_vector& es, dependency*& eqs); expr_ref expand(expr* e, dependency*& eqs); void add_dependency(dependency*& dep, enode* a, enode* b); From d7dcd022b997de6c7577b04005f16eb2207d1adc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Jan 2016 18:49:21 -0800 Subject: [PATCH 32/35] seq, API Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 83 ++++++++++++++++++++++++++++----------- src/api/ml/z3.ml | 82 ++++++++++++-------------------------- 2 files changed, 86 insertions(+), 79 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index f27402e91..40b597be4 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -103,6 +103,7 @@ public class Context extends IDisposable private BoolSort m_boolSort = null; private IntSort m_intSort = null; private RealSort m_realSort = null; + private SeqSort m_stringSort = null; /** * Retrieves the Boolean sort of the context. @@ -142,6 +143,16 @@ public class Context extends IDisposable return new BoolSort(this); } + /** + * Retrieves the Integer sort of the context. + **/ + public SeqSort getStringSort() + { + if (m_stringSort == null) + m_stringSort = mkStringSort(); + return m_stringSort; + } + /** * Create a new uninterpreted sort. **/ @@ -193,6 +204,31 @@ public class Context extends IDisposable return new ArraySort(this, domain, range); } + /** + * Create a new string sort + **/ + public SeqSort mkStringSort() + { + return new SeqSort(this, Native.mkStringSort(nCtx())); + } + + /** + * Create a new sequence sort + **/ + public SeqSort mkSeqSort(Sort s) + { + return new SeqSort(this, Native.mkSeqSort(nCtx(), s.getNativeObject())); + } + + /** + * Create a new regular expression sort + **/ + public ReSort mkReSort(Sort s) + { + return new ReSort(this, Native.mkReSort(nCtx(), s.getNativeObject())); + } + + /** * Create a new tuple sort. **/ @@ -1860,7 +1896,7 @@ public class Context extends IDisposable public SeqExpr MkEmptySeq(Sort s) { checkContextMatch(s); - return new SeqExpr(this, Native.mkSeqEmpty(nCtx, s.NativeObject)); + return new SeqExpr(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); } /** @@ -1869,24 +1905,24 @@ public class Context extends IDisposable public SeqExpr MkUnit(Expr elem) { checkContextMatch(elem); - return new SeqExpr(this, Native.mkSeqUnit(nCtx, elem.NativeObject)); + return new SeqExpr(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); } /** * Create a string constant. */ - public SeqExpr MkString(string s) + public SeqExpr MkString(String s) { - return new SeqExpr(this, Native.mkString(nCtx, s)); + return new SeqExpr(this, Native.mkString(nCtx(), s)); } /** * Concatentate sequences. */ - public SeqExpr MkConcat(params SeqExpr[] t) + public SeqExpr MkConcat(SeqExpr... t) { checkContextMatch(t); - return new SeqExpr(this, Native.mkSeqConcat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + return new SeqExpr(this, Native.mkSeqConcat(nCtx(), (int)t.length, AST.arrayToNative(t))); } @@ -1896,7 +1932,7 @@ public class Context extends IDisposable public IntExpr MkLength(SeqExpr s) { checkContextMatch(s); - return (IntExpr) Expr.Create(this, Native.mkSeqLength(nCtx, s.NativeObject)); + return (IntExpr) Expr.create(this, Native.mkSeqLength(nCtx(), s.getNativeObject())); } /** @@ -1905,7 +1941,7 @@ public class Context extends IDisposable public BoolExpr MkPrefixOf(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); - return new BoolExpr(this, Native.mkSeqPrefix(nCtx, s1.NativeObject, s2.NativeObject)); + return new BoolExpr(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** @@ -1914,7 +1950,7 @@ public class Context extends IDisposable public BoolExpr MkSuffixOf(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); - return new BoolExpr(this, Native.mkSeqSuffix(nCtx, s1.NativeObject, s2.NativeObject)); + return new BoolExpr(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** @@ -1923,7 +1959,7 @@ public class Context extends IDisposable public BoolExpr MkContains(SeqExpr s1, SeqExpr s2) { checkContextMatch(s1, s2); - return new BoolExpr(this, Native.mkSeqContains(nCtx, s1.NativeObject, s2.NativeObject)); + return new BoolExpr(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); } /** @@ -1932,7 +1968,7 @@ public class Context extends IDisposable public SeqExpr MkAt(SeqExpr s, IntExpr index) { checkContextMatch(s, index); - return new SeqExpr(this, Native.mkSeqAt(nCtx, s.NativeObject, index.NativeObject)); + return new SeqExpr(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); } /** @@ -1941,7 +1977,7 @@ public class Context extends IDisposable public SeqExpr MkExtract(SeqExpr s, IntExpr offset, IntExpr length) { checkContextMatch(s, offset, length); - return new SeqExpr(this, Native.mkSeqExtract(nCtx, s.NativeObject, offset.NativeObject, length.NativeObject)); + return new SeqExpr(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); } /** @@ -1950,7 +1986,7 @@ public class Context extends IDisposable public IntExpr MkIndexOf(SeqExpr s, SeqExpr substr, ArithExpr offset) { checkContextMatch(s, substr, offset); - return new IntExpr(this, Native.mkSeqIndex(nCtx, s.NativeObject, substr.NativeObject, offset.NativeObject)); + return new IntExpr(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); } /** @@ -1959,7 +1995,7 @@ public class Context extends IDisposable public SeqExpr MkReplace(SeqExpr s, SeqExpr src, SeqExpr dst) { checkContextMatch(s, src, dst); - return new SeqExpr(this, Native.mkSeqReplace(nCtx, s.NativeObject, src.NativeObject, dst.NativeObject)); + return new SeqExpr(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); } /** @@ -1968,7 +2004,7 @@ public class Context extends IDisposable public ReExpr MkToRe(SeqExpr s) { checkContextMatch(s); - return new ReExpr(this, Native.mkSeqToRe(nCtx, s.NativeObject)); + return new ReExpr(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); } @@ -1978,7 +2014,7 @@ public class Context extends IDisposable public BoolExpr MkInRe(SeqExpr s, ReExpr re) { checkContextMatch(s, re); - return new BoolExpr(this, Native.mkSeqInRe(nCtx, s.NativeObject, re.NativeObject)); + return new BoolExpr(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); } /** @@ -1987,7 +2023,7 @@ public class Context extends IDisposable public ReExpr MkStar(ReExpr re) { checkContextMatch(re); - return new ReExpr(this, Native.mkReStar(nCtx, re.NativeObject)); + return new ReExpr(this, Native.mkReStar(nCtx(), re.getNativeObject())); } /** @@ -1996,7 +2032,7 @@ public class Context extends IDisposable public ReExpr MPlus(ReExpr re) { checkContextMatch(re); - return new ReExpr(this, Native.mkRePlus(nCtx, re.NativeObject)); + return new ReExpr(this, Native.mkRePlus(nCtx(), re.getNativeObject())); } /** @@ -2005,25 +2041,25 @@ public class Context extends IDisposable public ReExpr MOption(ReExpr re) { checkContextMatch(re); - return new ReExpr(this, Native.mkReOption(nCtx, re.NativeObject)); + return new ReExpr(this, Native.mkReOption(nCtx(), re.getNativeObject())); } /** * Create the concatenation of regular languages. */ - public ReExpr MkConcat(ReExpr[] t) + public ReExpr MkConcat(ReExpr... t) { checkContextMatch(t); - return new ReExpr(this, Native.mkReConcat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + return new ReExpr(this, Native.mkReConcat(nCtx(), (int)t.length, AST.arrayToNative(t))); } /** * Create the union of regular languages. */ - public ReExpr MkUnion(ReExpr[] t) + public ReExpr MkUnion(ReExpr... t) { checkContextMatch(t); - return new ReExpr(this, Native.mkReUnion(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + return new ReExpr(this, Native.mkReUnion(nCtx(), (int)t.length, AST.arrayToNative(t))); } @@ -4021,6 +4057,7 @@ public class Context extends IDisposable m_boolSort = null; m_intSort = null; m_realSort = null; + m_stringSort = null; synchronized (creation_lock) { if (m_refCount.get() == 0 && m_ctx != 0) { diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 9a9b17500..54b9c3932 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -88,8 +88,6 @@ struct (z3obj_create res) ; res - let ignore2 a b c = ignore a; ignore b - let ignore3 a b c = ignore a; ignore2 b c end @@ -837,20 +835,11 @@ end = struct let o = Z3native.mk_app (context_gno ctx) (AST.ptr_of_ast fa) (List.length args) (expr_lton args) in expr_of_ptr ctx o - let apply_un ctx f t = - let r = expr_of_ptr ctx (f (context_gno ctx) (gno t)) in - ignore t; - r + let apply1 ctx f t = expr_of_ptr ctx (f (context_gno ctx) (gno t)) in - let apply_bin ctx f t1 t2 = - let r = expr_of_ptr ctx (f (context_gno ctx) (gno t1) (gno t2)) in - ignore2 t1 t2; - r + let apply2 ctx f t1 t2 = expr_of_ptr ctx (f (context_gno ctx) (gno t1) (gno t2)) in - let apply_ter ctx f t1 t2 t3 = - let r = expr_of_ptr ctx (f (context_gno ctx) (gno t1) (gno t2) (gno t3)) in - ignore3 t1 t2 t3; - r + let apply3 ctx f t1 t2 t3 = expr_of_ptr ctx (f (context_gno ctx) (gno t1) (gno t2) (gno t3)) in let simplify ( x : expr ) ( p : Params.params option ) = match p with @@ -874,17 +863,13 @@ end = struct if ((AST.is_app (ast_of_expr x)) && (List.length args <> (get_num_args x))) then raise (Z3native.Exception "Number of arguments does not match") else - let r = expr_of_ptr (Expr.gc x) (Z3native.update_term (gnc x) (gno x) (List.length args) (expr_lton args)) in - ignore2 x args; - r - + expr_of_ptr (Expr.gc x) (Z3native.update_term (gnc x) (gno x) (List.length args) (expr_lton args)) + let substitute ( x : expr ) from to_ = if (List.length from) <> (List.length to_) then raise (Z3native.Exception "Argument sizes do not match") else - let r = expr_of_ptr (Expr.gc x) (Z3native.substitute (gnc x) (gno x) (List.length from) (expr_lton from) (expr_lton to_)) in - ignore3 from to_ x; - r + expr_of_ptr (Expr.gc x) (Z3native.substitute (gnc x) (gno x) (List.length from) (expr_lton from) (expr_lton to_)) let substitute_one ( x : expr ) from to_ = substitute ( x : expr ) [ from ] [ to_ ] @@ -896,9 +881,7 @@ end = struct if (Expr.gc x) == to_ctx then x else - let r = expr_of_ptr to_ctx (Z3native.translate (gnc x) (gno x) (context_gno to_ctx)) in - ignore2 x to_ctx; - r + expr_of_ptr to_ctx (Z3native.translate (gnc x) (gno x) (context_gno to_ctx)) let to_string ( x : expr ) = Z3native.ast_to_string (gnc x) (gno x) @@ -959,39 +942,33 @@ struct let mk_val ( ctx : context ) ( value : bool ) = if value then mk_true ctx else mk_false ctx - let mk_not ( ctx : context ) ( a : expr ) = apply_un ctx Z3native.mk_not a + let mk_not ( ctx : context ) ( a : expr ) = apply1 ctx Z3native.mk_not a let mk_ite ( ctx : context ) ( t1 : expr ) ( t2 : expr ) ( t3 : expr ) = - apply_ter ctx Z3native.mk_ite t1 t2 t3 + apply3 ctx Z3native.mk_ite t1 t2 t3 let mk_iff ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - apply_bin ctx Z3native.mk_iff t1 t2 + apply2 ctx Z3native.mk_iff t1 t2 let mk_implies ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - apply_bin ctx Z3native.mk_implies t1 t2 + apply2 ctx Z3native.mk_implies t1 t2 let mk_xor ( ctx : context ) ( t1 : expr ) ( t2 : expr ) = - apply_bin ctx Z3native.mk_xor t1 t2 + apply2 ctx Z3native.mk_xor t1 t2 let mk_and ( ctx : context ) ( args : expr list ) = let f x = (Expr.gno (x)) in - let r = expr_of_ptr ctx (Z3native.mk_and (context_gno ctx) (List.length args) (Array.of_list (List.map f args))) in - ignore args; - r + expr_of_ptr ctx (Z3native.mk_and (context_gno ctx) (List.length args) (Array.of_list (List.map f args))) let mk_or ( ctx : context ) ( args : expr list ) = let f x = (Expr.gno (x)) in - let r = expr_of_ptr ctx (Z3native.mk_or (context_gno ctx) (List.length args) (Array.of_list(List.map f args))) in - ignore args; - r + expr_of_ptr ctx (Z3native.mk_or (context_gno ctx) (List.length args) (Array.of_list(List.map f args))) let mk_eq ( ctx : context ) ( x : expr ) ( y : expr ) = - apply_bin ctx Z3native.mk_eq x y + apply2 ctx Z3native.mk_eq x y let mk_distinct ( ctx : context ) ( args : expr list ) = - let r = expr_of_ptr ctx (Z3native.mk_distinct (context_gno ctx) (List.length args) (expr_lton args)) in - ignore args; - r + expr_of_ptr ctx (Z3native.mk_distinct (context_gno ctx) (List.length args) (expr_lton args)) let get_bool_value ( x : expr ) = lbool_of_int (Z3native.get_bool_value (gnc x) (gno x)) @@ -1097,12 +1074,10 @@ struct mk_list f n let get_body ( x : quantifier ) = - apply_un (gc x) Z3native.get_quantifier_body x + apply1 (gc x) Z3native.get_quantifier_body x let mk_bound ( ctx : context ) ( index : int ) ( ty : Sort.sort ) = - let r = expr_of_ptr ctx (Z3native.mk_bound (context_gno ctx) index (Sort.gno ty)) in - ignore ty; - r + expr_of_ptr ctx (Z3native.mk_bound (context_gno ctx) index (Sort.gno ty)) let mk_pattern ( ctx : context ) ( terms : expr list ) = if (List.length terms) == 0 then @@ -1227,27 +1202,23 @@ struct mk_const ctx (Symbol.mk_string ctx name) domain range let mk_select ( ctx : context ) ( a : expr ) ( i : expr ) = - apply_bin ctx Z3native.mk_select a i + apply2 ctx Z3native.mk_select a i let mk_store ( ctx : context ) ( a : expr ) ( i : expr ) ( v : expr ) = - apply_ter ctx Z3native.mk_store a i v + apply3 ctx Z3native.mk_store a i v let mk_const_array ( ctx : context ) ( domain : Sort.sort ) ( v : expr ) = - let r = expr_of_ptr ctx (Z3native.mk_const_array (context_gno ctx) (Sort.gno domain) (Expr.gno v)) in - ignore2 domain v; - r + expr_of_ptr ctx (Z3native.mk_const_array (context_gno ctx) (Sort.gno domain) (Expr.gno v)) let mk_map ( ctx : context ) ( f : func_decl ) ( args : expr list ) = let m x = (Expr.gno x) in - let r = expr_of_ptr ctx (Z3native.mk_map (context_gno ctx) (FuncDecl.gno f) (List.length args) (Array.of_list (List.map m args))) in - ignore2 f args; - r + expr_of_ptr ctx (Z3native.mk_map (context_gno ctx) (FuncDecl.gno f) (List.length args) (Array.of_list (List.map m args))) let mk_term_array ( ctx : context ) ( arg : expr ) = - apply_un ctx Z3native.mk_array_default arg + apply1 ctx Z3native.mk_array_default arg let mk_array_ext ( ctx : context) ( arg1 : expr ) ( arg2 : expr ) = - apply_bin ctx Z3native.mk_array_ext arg1 arg2 + apply2 ctx Z3native.mk_array_ext arg1 arg2 end @@ -1270,14 +1241,13 @@ struct expr_of_ptr ctx (Z3native.mk_full_set (context_gno ctx) (Sort.gno domain)) let mk_set_add ( ctx : context ) ( set : expr ) ( element : expr ) = - apply_bin ctx Z3native.mk_set_add set element + apply2 ctx Z3native.mk_set_add set element let mk_del ( ctx : context ) ( set : expr ) ( element : expr ) = - apply_bin Z3native.mk_set_del set element + apply2 Z3native.mk_set_del set element let mk_union ( ctx : context ) ( args : expr list ) = let r = expr_of_ptr ctx (Z3native.mk_set_union (context_gno ctx) (List.length args) (expr_lton args)) in - ignore r; r let mk_intersection ( ctx : context ) ( args : expr list ) = From 2c1d2aad44b340da2aab15639a9f0acc6b50f7c9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Jan 2016 22:06:32 -0800 Subject: [PATCH 33/35] seq, API Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 33 ++++++++----------------------- src/smt/theory_seq.cpp | 16 ++++++++++----- src/smt/theory_seq.h | 1 + 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 1231369c7..3ee8675d8 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -1001,7 +1001,7 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ } if (l < s2.length()) { rs.push_back(m_util.str.mk_string(s2.extract(0, s2.length()-l))); - } + } change = true; } @@ -1023,27 +1023,17 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ } else if (!change) { // skip + SASSERT(lhs.empty()); } else { // could solve if either side is fixed size. SASSERT(szl > 0 && szr > 0); - if (head1 > 0) { - for (unsigned i = 0; i < szl; ++i) { - ls[i] = ls[i + head1]; - } - } - ls.shrink(szl); - if (head2 > 0) { - for (unsigned i = 0; i < szr; ++i) { - rs[i] = rs[i + head2]; - } - } - rs.shrink(szr); - lhs.push_back(m_util.str.mk_concat(ls.size(), ls.c_ptr())); - rhs.push_back(m_util.str.mk_concat(rs.size(), rs.c_ptr())); + lhs.push_back(m_util.str.mk_concat(szl, ls.c_ptr() + head1)); + rhs.push_back(m_util.str.mk_concat(szr, rs.c_ptr() + head2)); ls.reset(); rs.reset(); } + SASSERT(lhs.empty() || ls.empty()); return true; } @@ -1054,16 +1044,9 @@ bool seq_rewriter::reduce_eq(expr* l, expr* r, expr_ref_vector& lhs, expr_ref_ve m_util.str.get_concat(r, m_rhs); if (reduce_eq(m_lhs, m_rhs, lhs, rhs)) { SASSERT(lhs.size() == rhs.size()); - if (!m_lhs.empty()) { - SASSERT(!m_rhs.empty()); - lhs.push_back(m_util.str.mk_concat(m_lhs.size(), m_lhs.c_ptr())); - rhs.push_back(m_util.str.mk_concat(m_rhs.size(), m_rhs.c_ptr())); - } - for (unsigned i = 0; i < lhs.size(); ++i) { - SASSERT(is_well_sorted(m(), lhs[i].get())); - SASSERT(is_well_sorted(m(), rhs[i].get())); - SASSERT(m().get_sort(lhs[i].get()) == m().get_sort(rhs[i].get())); - TRACE("seq", tout << mk_pp(lhs[i].get(), m()) << " = " << mk_pp(rhs[i].get(), m()) << "\n";); + if (lhs.empty()) { + lhs.push_back(l); + rhs.push_back(r); } return true; } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 7aa434661..e3ede010c 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -166,6 +166,8 @@ theory_seq::theory_seq(ast_manager& m): m_autil(m), m_trail_stack(*this), m_atoms_qhead(0), + m_ls(m), m_rs(m), + m_lhs(m), m_rhs(m), m_new_solution(false), m_new_propagation(false) { m_prefix = "seq.prefix.suffix"; @@ -874,17 +876,21 @@ void theory_seq::solve_ne(unsigned idx) { } } for (unsigned i = 0; i < n.m_lhs.size(); ++i) { - expr_ref_vector lhs(m), rhs(m); + expr_ref_vector& ls = m_ls; + expr_ref_vector& rs = m_rs; + expr_ref_vector& lhs = m_lhs; + expr_ref_vector& rhs = m_rhs; + ls.reset(); rs.reset(); lhs.reset(); rhs.reset(); dependency* deps = 0; expr* l = n.m_lhs[i]; expr* r = n.m_rhs[i]; - expr_ref lh = canonize(l, deps); - expr_ref rh = canonize(r, deps); - if (!rw.reduce_eq(lh, rh, lhs, rhs)) { + canonize(l, ls, deps); + canonize(r, rs, deps); + if (!rw.reduce_eq(ls, rs, lhs, rhs)) { mark_solved(idx); return; } - else if (unchanged(l, lhs, r, rhs) ) { + else if (lhs.empty() || (lhs.size() == 1 && lhs[0].get() == l)) { // continue } else { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 511c6917c..cd8d67cd1 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -318,6 +318,7 @@ namespace smt { symbol m_tail, m_nth, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; symbol m_extract_prefix, m_at_left, m_at_right; ptr_vector m_todo; + expr_ref_vector m_ls, m_rs, m_lhs, m_rhs; // maintain automata with regular expressions. scoped_ptr_vector m_automata; From 2f9fda45c3e90fcfa0127448951cda2e4550b244 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Jan 2016 22:14:45 -0800 Subject: [PATCH 34/35] fix tabs Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 103 +++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 9129cb854..1a9ef204b 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -931,9 +931,9 @@ def _to_expr_ref(a, ctx): if sk == Z3_ROUNDING_MODE_SORT: return FPRMRef(a, ctx) if sk == Z3_SEQ_SORT: - return SeqRef(a, ctx) + return SeqRef(a, ctx) if sk == Z3_RE_SORT: - return ReRef(a, ctx) + return ReRef(a, ctx) return ExprRef(a, ctx) def _coerce_expr_merge(s, a): @@ -3573,24 +3573,24 @@ def Concat(*args): ctx = args[0].ctx if is_seq(args[0]): - if __debug__: - _z3_assert(all([is_seq(a) for a in args]), "All arguments must be sequence expressions.") - v = (Ast * sz)() + if __debug__: + _z3_assert(all([is_seq(a) for a in args]), "All arguments must be sequence expressions.") + v = (Ast * sz)() for i in range(sz): v[i] = args[i].as_ast() - return SeqRef(Z3_mk_seq_concat(ctx.ref(), sz, v), ctx) - + return SeqRef(Z3_mk_seq_concat(ctx.ref(), sz, v), ctx) + if is_re(args[0]): - if __debug__: - _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") - v = (Ast * sz)() - for i in range(sz): - v[i] = args[i].as_ast() - return ReRef(Z3_mk_re_concat(ctx.ref(), sz, v), ctx) - + if __debug__: + _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") + v = (Ast * sz)() + for i in range(sz): + v[i] = args[i].as_ast() + return ReRef(Z3_mk_re_concat(ctx.ref(), sz, v), ctx) + if __debug__: _z3_assert(all([is_bv(a) for a in args]), "All arguments must be Z3 bit-vector expressions.") - r = args[0] + r = args[0] for i in range(sz - 1): r = BitVecRef(Z3_mk_concat(ctx.ref(), r.as_ast(), args[i+1].as_ast()), ctx) return r @@ -3607,14 +3607,14 @@ def Extract(high, low, a): "c" """ if isinstance(high, str): - high = StringVal(high) + high = StringVal(high) if is_seq(high): - s = high - offset = _py2expr(low, high.ctx) - length = _py2expr(a, high.ctx) - - if __debug__: - _z3_assert(is_int(offset) and is_int(length), "Second and third arguments must be integers") + s = high + offset = _py2expr(low, high.ctx) + length = _py2expr(a, high.ctx) + + if __debug__: + _z3_assert(is_int(offset) and is_int(length), "Second and third arguments must be integers") return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) if __debug__: _z3_assert(low <= high, "First argument must be greater than or equal to second argument") @@ -8951,15 +8951,15 @@ class SeqSortRef(SortRef): """Sequence sort.""" def is_string(self): - """Determine if sort is a string - >>> s = StringSort() - >>> s.is_string() - True - >>> s = SeqSort(IntSort()) - >>> s.is_string() - False - """ - return Z3_is_string_sort(self.ctx_ref(), self.ast) + """Determine if sort is a string + >>> s = StringSort() + >>> s.is_string() + True + >>> s = SeqSort(IntSort()) + >>> s.is_string() + False + """ + return Z3_is_string_sort(self.ctx_ref(), self.ast) def StringSort(ctx=None): """Create a string sort @@ -8983,19 +8983,19 @@ class SeqRef(ExprRef): """Sequence expression.""" def sort(self): - return SeqSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) + return SeqSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) def __add__(self, other): - return Concat(self, other) + return Concat(self, other) def __getitem__(self, i): - return SeqRef(Z3_mk_seq_at(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) - + return SeqRef(Z3_mk_seq_at(self.ctx_ref(), self.as_ast(), i.as_ast()), self.ctx) + def is_string(self): - return Z3_is_string_sort(self.ctx_ref(), Z3_get_sort(self.ctx_ref(), self.as_ast())) + return Z3_is_string_sort(self.ctx_ref(), Z3_get_sort(self.ctx_ref(), self.as_ast())) def is_string_value(self): - return Z3_is_string(self.ctx_ref(), self.as_ast()) + return Z3_is_string(self.ctx_ref(), self.as_ast()) def as_string(self): """Return a string representation of sequence expression.""" @@ -9004,17 +9004,17 @@ class SeqRef(ExprRef): def _coerce_seq(s, ctx=None): if isinstance(s, str): - ctx = _get_ctx(ctx) - s = StringVal(s, ctx) + ctx = _get_ctx(ctx) + s = StringVal(s, ctx) return s def _get_ctx2(a, b, ctx=None): if is_expr(a): - return a.ctx + return a.ctx if is_expr(b): - return b.ctx + return b.ctx if ctx is None: - ctx = main_ctx() + ctx = main_ctx() return ctx def is_seq(a): @@ -9136,7 +9136,7 @@ def Replace(s, src, dst): """ ctx = _get_ctx2(dst, s) if ctx is None and is_expr(src): - ctx = src.ctx + ctx = src.ctx src = _coerce_seq(src, ctx) dst = _coerce_seq(dst, ctx) s = _coerce_seq(s, ctx) @@ -9154,12 +9154,12 @@ def IndexOf(s, substr, offset): """ ctx = None if is_expr(offset): - ctx = offset.ctx + ctx = offset.ctx ctx = _get_ctx2(s, substr, ctx) s = _coerce_seq(s, ctx) substr = _coerce_seq(substr, ctx) if isinstance(offset, int): - offset = IntVal(offset, ctx) + offset = IntVal(offset, ctx) return SeqRef(Z3_mk_seq_index(s.ctx_ref(), s.as_ast(), substr.as_ast(), offset.as_ast()), s.ctx) def Length(s): @@ -9191,10 +9191,10 @@ class ReSortRef(SortRef): def ReSort(s): if is_ast(s): - return ReSortRef(Z3_mk_re_sort(s.ctx.ref(), s.as_ast()), ctx) + return ReSortRef(Z3_mk_re_sort(s.ctx.ref(), s.as_ast()), ctx) if s is None or isinstance(s, Context): - ctx = _get_ctx(s) - return ReSortRef(Z3_mk_re_sort(ctx.ref(), Z3_mk_string_sort(ctx.ref())), ctx) + ctx = _get_ctx(s) + return ReSortRef(Z3_mk_re_sort(ctx.ref(), Z3_mk_string_sort(ctx.ref())), ctx) raise Z3Exception("Regular expression sort constructor expects either a string or a context or no argument") @@ -9202,7 +9202,7 @@ class ReRef(ExprRef): """Regular expressions.""" def __add__(self, other): - return Union(self, other) + return Union(self, other) def is_re(s): @@ -9232,13 +9232,12 @@ def Union(*args): sz = len(args) if __debug__: _z3_assert(sz >= 2, "At least two arguments expected.") - _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") + _z3_assert(all([is_re(a) for a in args]), "All arguments must be regular expressions.") ctx = args[0].ctx v = (Ast * sz)() for i in range(sz): - v[i] = args[i].as_ast() + v[i] = args[i].as_ast() return ReRef(Z3_mk_re_union(ctx.ref(), sz, v), ctx) - def Plus(re): """Create the regular expression accepting one or more repetitions of argument. From 3f040dbd23b7a3dc622c31c6ca51aef30f073a29 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 4 Jan 2016 22:26:54 -0800 Subject: [PATCH 35/35] remove std::cout usage Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/expr_safe_replace.cpp | 1 - src/muz/rel/dl_mk_simple_joins.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ast/rewriter/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp index 8ce40e49a..25a8fd657 100644 --- a/src/ast/rewriter/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -76,7 +76,6 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { } } else { - (std::cout << "q\n").flush(); SASSERT(is_quantifier(a)); quantifier* q = to_quantifier(a); expr_safe_replace replace(m); diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index b6fe39617..b9febe53e 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -273,7 +273,6 @@ namespace datalog { */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { SASSERT(t1!=t2); - std::cout << "insert: " << mk_pp(t1, m) << " - " << mk_pp(t2, m) << "\n"; cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); pair_info * & ptr_inf = e->get_data().m_value; if (ptr_inf==0) {