diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index b8a2d3ce5..906b647fe 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -33,20 +33,23 @@ Notes: expr_ref sym_expr::accept(expr* e) { ast_manager& m = m_t.get_manager(); expr_ref result(m); + var_subst subst(m); + seq_util u(m); + unsigned r1, r2, r3; switch (m_ty) { - case t_pred: { - var_subst subst(m); + case t_pred: result = subst(m_t, 1, &e); + break; + case t_not: + result = m_expr->accept(e); + result = m.mk_not(result); break; - } case t_char: SASSERT(m.get_sort(e) == m.get_sort(m_t)); SASSERT(m.get_sort(e) == m_sort); result = m.mk_eq(e, m_t); break; - case t_range: { - seq_util u(m); - unsigned r1, r2, r3; + case t_range: if (u.is_const_char(m_t, r1) && u.is_const_char(e, r2) && u.is_const_char(m_s, r3)) { result = m.mk_bool_val((r1 <= r2) && (r2 <= r3)); } @@ -55,7 +58,7 @@ expr_ref sym_expr::accept(expr* e) { } break; } - } + return result; } @@ -64,6 +67,7 @@ std::ostream& sym_expr::display(std::ostream& out) const { case t_char: return out << m_t; case t_range: return out << m_t << ":" << m_s; case t_pred: return out << m_t; + case t_not: return m_expr->display(out << "not "); } return out << "expression type not recognized"; } @@ -79,10 +83,11 @@ struct display_expr1 { class sym_expr_boolean_algebra : public boolean_algebra { ast_manager& m; expr_solver& m_solver; + expr_ref m_var; typedef sym_expr* T; public: sym_expr_boolean_algebra(ast_manager& m, expr_solver& s): - m(m), m_solver(s) {} + m(m), m_solver(s), m_var(m) {} T mk_false() override { expr_ref fml(m.mk_false(), m); @@ -93,6 +98,7 @@ public: return sym_expr::mk_pred(fml, m.mk_bool_sort()); } T mk_and(T x, T y) override { + seq_util u(m); if (x->is_char() && y->is_char()) { if (x->get_char() == y->get_char()) { return x; @@ -102,6 +108,21 @@ public: return sym_expr::mk_pred(fml, x->get_sort()); } } + unsigned lo1, hi1, lo2, hi2; + if (x->is_range() && y->is_range() && + u.is_const_char(x->get_lo(), lo1) && u.is_const_char(x->get_hi(), hi1) && + u.is_const_char(y->get_lo(), lo2) && u.is_const_char(y->get_hi(), hi2)) { + lo1 = std::max(lo1, lo2); + hi1 = std::min(hi1, hi2); + if (lo1 > hi1) { + expr_ref fml(m.mk_false(), m); + return sym_expr::mk_pred(fml, x->get_sort()); + } + expr_ref _start(u.mk_char(lo1), m); + expr_ref _stop(u.mk_char(hi1), m); + return sym_expr::mk_range(_start, _stop); + } + sort* s = x->get_sort(); if (m.is_bool(s)) s = y->get_sort(); var_ref v(m.mk_var(0, s), m); @@ -110,13 +131,29 @@ public: if (m.is_true(fml1)) { return y; } - if (m.is_true(fml2)) return x; - if (fml1 == fml2) return x; + if (m.is_true(fml2)) { + return x; + } + if (fml1 == fml2) { + return x; + } + if (is_complement(fml1, fml2)) { + expr_ref ff(m.mk_false(), m); + return sym_expr::mk_pred(ff, x->get_sort()); + } bool_rewriter br(m); expr_ref fml(m); br.mk_and(fml1, fml2, fml); return sym_expr::mk_pred(fml, x->get_sort()); } + + bool is_complement(expr* f1, expr* f2) { + expr* f = nullptr; + return + (m.is_not(f1, f) && f == f2) || + (m.is_not(f2, f) && f == f1); + } + T mk_or(T x, T y) override { if (x->is_char() && y->is_char() && x->get_char() == y->get_char()) { @@ -147,6 +184,7 @@ public: } } } + T mk_or(unsigned sz, T const* ts) override { switch (sz) { case 0: return mk_false(); @@ -160,15 +198,24 @@ public: } } } + lbool is_sat(T x) override { + unsigned lo, hi; + seq_util u(m); + if (x->is_char()) { return l_true; } - if (x->is_range()) { - // TBD check lower is below upper. + if (x->is_range() && u.is_const_char(x->get_lo(), lo) && u.is_const_char(x->get_hi(), hi)) { + return (lo <= hi) ? l_true : l_false; } - expr_ref v(m.mk_fresh_const("x", x->get_sort()), m); - expr_ref fml = x->accept(v); + if (x->is_not() && x->get_arg()->is_range() && u.is_const_char(x->get_arg()->get_lo(), lo) && 0 < lo) { + return l_true; + } + if (!m_var || m.get_sort(m_var) != x->get_sort()) { + m_var = m.mk_fresh_const("x", x->get_sort()); + } + expr_ref fml = x->accept(m_var); if (m.is_true(fml)) { return l_true; } @@ -177,16 +224,11 @@ public: } return m_solver.check_sat(fml); } + T mk_not(T x) override { - var_ref v(m.mk_var(0, x->get_sort()), m); - expr_ref fml(m.mk_not(x->accept(v)), m); - return sym_expr::mk_pred(fml, x->get_sort()); + return sym_expr::mk_not(m, x); } - /*virtual vector, T>> generate_min_terms(vector constraints){ - - return 0; - }*/ }; re2automaton::re2automaton(ast_manager& m): m(m), u(m), m_ba(nullptr), m_sa(nullptr) {} @@ -1434,6 +1476,14 @@ br_status seq_rewriter::mk_re_inter(expr* a, expr* b, expr_ref& result) { result = a; return BR_DONE; } + expr* ac = nullptr, *bc = nullptr; + if ((m_util.re.is_complement(a, ac) && ac == b) || + (m_util.re.is_complement(b, bc) && bc == a)) { + sort* seq_sort = nullptr; + VERIFY(m_util.is_re(a, seq_sort)); + result = m_util.re.mk_empty(seq_sort); + return BR_DONE; + } return BR_FAILED; } diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index c2b2227b2..83793a594 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -31,31 +31,38 @@ class sym_expr { enum ty { t_char, t_pred, + t_not, t_range }; - ty m_ty; - sort* m_sort; - expr_ref m_t; - expr_ref m_s; - unsigned m_ref; - sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt) : m_ty(ty), m_sort(srt), m_t(t), m_s(s), m_ref(0) {} + ty m_ty; + sort* m_sort; + sym_expr* m_expr; + expr_ref m_t; + expr_ref m_s; + unsigned m_ref; + sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt, sym_expr* e) : + m_ty(ty), m_sort(srt), m_expr(e), m_t(t), m_s(s), m_ref(0) {} public: + ~sym_expr() { if (m_expr) m_expr->dec_ref(); } expr_ref accept(expr* e); - static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t)); } + static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t), nullptr); } static sym_expr* mk_char(ast_manager& m, expr* t) { expr_ref tr(t, m); return mk_char(tr); } - static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s); } - static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi)); } + static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s, nullptr); } + static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi), nullptr); } + static sym_expr* mk_not(ast_manager& m, sym_expr* e) { expr_ref f(m); e->inc_ref(); return alloc(sym_expr, t_not, f, f, e->get_sort(), e); } void inc_ref() { ++m_ref; } void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); } std::ostream& display(std::ostream& out) const; bool is_char() const { return m_ty == t_char; } bool is_pred() const { return !is_char(); } bool is_range() const { return m_ty == t_range; } + bool is_not() const { return m_ty == t_not; } sort* get_sort() const { return m_sort; } expr* get_char() const { SASSERT(is_char()); return m_t; } expr* get_pred() const { SASSERT(is_pred()); return m_t; } expr* get_lo() const { SASSERT(is_range()); return m_t; } expr* get_hi() const { SASSERT(is_range()); return m_s; } + sym_expr* get_arg() const { SASSERT(is_not()); return m_expr; } }; class sym_expr_manager { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 2f7a82f2d..19ef14c3a 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -51,6 +51,7 @@ public: m_kernel.assert_expr(e); lbool r = m_kernel.check(); m_kernel.pop(1); + IF_VERBOSE(11, verbose_stream() << "is " << r << " " << mk_pp(e, m_kernel.m()) << "\n"); return r; } }; @@ -250,6 +251,9 @@ void theory_seq::init(context* ctx) { m_arith_value.init(ctx); } +#define TRACEFIN(s) { TRACE("seq", tout << ">>" << s << "\n";); IF_VERBOSE(10, verbose_stream() << s << "\n"); } + + final_check_status theory_seq::final_check_eh() { if (m_reset_cache) { m_rep.reset_cache(); @@ -260,79 +264,79 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq_verbose", get_context().display(tout);); if (simplify_and_solve_eqs()) { ++m_stats.m_solve_eqs; - TRACE("seq", tout << ">>solve_eqs\n";); + TRACEFIN("solve_eqs"); return FC_CONTINUE; } if (check_contains()) { ++m_stats.m_propagate_contains; - TRACE("seq", tout << ">>propagate_contains\n";); + TRACEFIN("propagate_contains"); return FC_CONTINUE; } if (solve_nqs(0)) { ++m_stats.m_solve_nqs; - TRACE("seq", tout << ">>solve_nqs\n";); + TRACEFIN("solve_nqs"); return FC_CONTINUE; } if (fixed_length(true)) { ++m_stats.m_fixed_length; - TRACE("seq", tout << ">>zero_length\n";); + TRACEFIN("zero_length"); return FC_CONTINUE; } if (m_params.m_split_w_len && len_based_split()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>split_based_on_length\n";); + TRACEFIN("split_based_on_length"); return FC_CONTINUE; } if (fixed_length()) { ++m_stats.m_fixed_length; - TRACE("seq", tout << ">>fixed_length\n";); + TRACEFIN("fixed_length"); return FC_CONTINUE; } if (check_int_string()) { ++m_stats.m_int_string; - TRACE("seq", tout << ">>int_string\n";); + TRACEFIN("int_string"); return FC_CONTINUE; } if (reduce_length_eq()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>reduce length\n";); + TRACEFIN("reduce_length"); return FC_CONTINUE; } if (branch_unit_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_unit_variable\n";); + TRACEFIN("ranch_unit_variable"); return FC_CONTINUE; } if (branch_binary_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_binary_variable\n";); + TRACEFIN("branch_binary_variable"); return FC_CONTINUE; } if (branch_ternary_variable1() || branch_ternary_variable2() || branch_quat_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>split_based_on_alignment\n";); + TRACEFIN("split_based_on_alignment"); return FC_CONTINUE; } if (branch_variable_mb() || branch_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_variable\n";); + TRACEFIN("branch_variable"); return FC_CONTINUE; } if (check_length_coherence()) { ++m_stats.m_check_length_coherence; - TRACE("seq", tout << ">>check_length_coherence\n";); + TRACEFIN("check_length_coherence"); return FC_CONTINUE; } if (!check_extensionality()) { ++m_stats.m_extensionality; - TRACE("seq", tout << ">>extensionality\n";); + TRACEFIN("extensionality"); return FC_CONTINUE; } if (is_solved()) { - TRACE("seq", tout << ">>is_solved\n";); + TRACEFIN("is_solved"); return FC_DONE; } - TRACE("seq", tout << ">>give_up\n";); + TRACEFIN("give_up"); return FC_GIVEUP; } @@ -4518,8 +4522,6 @@ void theory_seq::add_itos_length_axiom(expr* len) { void theory_seq::propagate_in_re(expr* n, bool is_true) { TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); - expr* s = nullptr, *re = nullptr; - VERIFY(m_util.str.is_in_re(n, s, re)); expr_ref tmp(n, m); m_rewrite(tmp); @@ -4540,43 +4542,39 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { return; } - expr_ref e3(re, m); + expr* s = nullptr, *_re = nullptr; + VERIFY(m_util.str.is_in_re(n, s, _re)); + expr_ref re(_re, m); context& ctx = get_context(); literal lit = ctx.get_literal(n); if (!is_true) { - e3 = m_util.re.mk_complement(re); + re = m_util.re.mk_complement(re); lit.neg(); } - literal_vector lits; - - enode_pair_vector eqs; + literal_vector lits; for (unsigned i = 0; i < m_s_in_re.size(); ++i) { auto const& entry = m_s_in_re[i]; - if (entry.m_active && get_root(entry.m_s) == get_root(s)) { + if (entry.m_active && get_root(entry.m_s) == get_root(s) && entry.m_re != re) { m_trail_stack.push(vector_value_trail(m_s_in_re, i)); m_s_in_re[i].m_active = false; - e3 = m_util.re.mk_inter(entry.m_re, e3); + IF_VERBOSE(11, verbose_stream() << "intersect " << re << " " << mk_pp(entry.m_re, m) << " " << mk_pp(s, m) << " " << mk_pp(entry.m_s, m) << "\n";); + re = m_util.re.mk_inter(entry.m_re, re); + m_rewrite(re); lits.push_back(entry.m_lit); - eqs.push_back(enode_pair(ensure_enode(entry.m_s), ensure_enode(s))); + enode* n1 = ensure_enode(entry.m_s); + enode* n2 = ensure_enode(s); + if (n1 != n2) { + lits.push_back(mk_eq(n1->get_owner(), n2->get_owner(), false)); + } } } - if (!lits.empty()) { - TRACE("seq", tout << "creating intersection " << e3 << "\n";); - lits.push_back(lit); - literal inter = mk_literal(m_util.re.mk_in_re(s, e3)); - justification* js = - ctx.mk_justification( - ext_theory_propagation_justification( - get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), inter)); - ctx.assign(inter, js); - return; - } - eautomaton* a = get_automaton(e3); + IF_VERBOSE(11, verbose_stream() << mk_pp(s, m) << " in " << re << "\n"); + eautomaton* a = get_automaton(re); if (!a) return; - m_s_in_re.push_back(s_in_re(lit, s, e3, a)); + m_s_in_re.push_back(s_in_re(lit, s, re, a)); m_trail_stack.push(push_back_vector>(m_s_in_re)); expr_ref len = mk_len(s); @@ -4587,7 +4585,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { lits.push_back(~lit); for (unsigned st : states) { - lits.push_back(mk_accept(s, zero, e3, st)); + lits.push_back(mk_accept(s, zero, re, st)); } if (lits.size() == 2) { propagate_lit(nullptr, 1, &lit, lits[1]); @@ -4906,24 +4904,36 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { void theory_seq::add_at_axiom(expr* e) { expr* s = nullptr, *i = nullptr; VERIFY(m_util.str.is_at(e, s, i)); - expr_ref len_e = mk_len(e); - expr_ref len_s = mk_len(s); expr_ref zero(m_autil.mk_int(0), m); expr_ref one(m_autil.mk_int(1), m); - expr_ref x = mk_skolem(m_pre, s, i); - //expr_ref y = mk_skolem(m_post, s, mk_sub(mk_sub(len_s, i), one)); - expr_ref y = mk_skolem(m_tail, s, i); - expr_ref xey = mk_concat(x, e, y); - expr_ref len_x = mk_len(x); expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); - + expr_ref len_s = mk_len(s); literal i_ge_0 = mk_simplified_literal(m_autil.mk_ge(i, zero)); literal i_ge_len_s = mk_simplified_literal(m_autil.mk_ge(mk_sub(i, mk_len(s)), zero)); - - add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey)); - add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false)); - add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); + rational iv; + if (m_autil.is_numeral(i, iv) && iv.is_int() && !iv.is_neg()) { + expr_ref_vector es(m); + expr_ref nth(m); + unsigned k = iv.get_unsigned(); + for (unsigned j = 0; j <= k; ++j) { + es.push_back(m_util.str.mk_unit(mk_nth(s, m_autil.mk_int(j)))); + } + nth = es.back(); + es.push_back(mk_skolem(m_tail, s, m_autil.mk_int(k))); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, m_util.str.mk_concat(es))); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(nth, e)); + } + else { + expr_ref len_e = mk_len(e); + expr_ref x = mk_skolem(m_pre, s, i); + expr_ref y = mk_skolem(m_tail, s, i); + expr_ref xey = mk_concat(x, e, y); + expr_ref len_x = mk_len(x); + add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, xey)); + add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e, false)); + add_axiom(~i_ge_0, i_ge_len_s, mk_eq(i, len_x, false)); + } add_axiom(i_ge_0, mk_eq(e, emp, false)); add_axiom(~i_ge_len_s, mk_eq(e, emp, false));