diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index 44389eb6c..3ef433138 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -769,6 +769,43 @@ namespace seq { } } + void axioms::ubv2s_axiom(expr* b, unsigned k) { + expr_ref ge10k(m), ge10k1(m), eq(m); + bv_util bv(m); + sort* bv_sort = b->get_sort(); + rational pow(1); + for (unsigned i = 0; i < k; ++i) + pow *= 10; + if (k == 0) { + ge10k = m.mk_true(); + } + else { + ge10k = bv.mk_ule(bv.mk_numeral(pow, bv_sort), b); + } + ge10k1 = bv.mk_ule(bv.mk_numeral(pow * 10, bv_sort), b); + expr_ref_vector es(m); + expr_ref bb(b, m); + unsigned sz = bv.get_bv_size(b); + for (unsigned i = 0; i < k; ++i) { + es.push_back(seq.str.mk_unit(m_sk.mk_ubv2ch(bv.mk_bv_urem(bb, bv.mk_numeral(10, sz))))); + } + es.reverse(); + eq = m.mk_eq(seq.str.mk_ubv2s(b), seq.str.mk_concat(es, seq.str.mk_string_sort())); + add_clause(~ge10k, ge10k1, eq); + ubv2ch_axiom(bv_sort); + + } + + void axioms::ubv2ch_axiom(sort* bv_sort) { + bv_util bv(m); + expr_ref eq(m); + unsigned sz = bv.get_bv_size(bv_sort); + for (unsigned i = 0; i < 10; ++i) { + eq = m.mk_eq(m_sk.mk_ubv2ch(bv.mk_numeral(i, sz)), seq.mk_char('0' + i)); + add_clause(eq); + } + } + /** Let s := itos(e) diff --git a/src/ast/rewriter/seq_axioms.h b/src/ast/rewriter/seq_axioms.h index d124dd39f..8c2af65e4 100644 --- a/src/ast/rewriter/seq_axioms.h +++ b/src/ast/rewriter/seq_axioms.h @@ -94,6 +94,8 @@ namespace seq { void stoi_axiom(expr* n); void stoi_axiom(expr* e, unsigned k); void itos_axiom(expr* s, unsigned k); + void ubv2s_axiom(expr* b, unsigned k); + void ubv2ch_axiom(sort* bv_sort); void lt_axiom(expr* n); void le_axiom(expr* n); void is_digit_axiom(expr* n); diff --git a/src/ast/rewriter/seq_eq_solver.cpp b/src/ast/rewriter/seq_eq_solver.cpp index 2c595090e..e293743f8 100644 --- a/src/ast/rewriter/seq_eq_solver.cpp +++ b/src/ast/rewriter/seq_eq_solver.cpp @@ -16,6 +16,7 @@ Author: --*/ #include "ast/rewriter/seq_eq_solver.h" +#include "ast/bv_decl_plugin.h" namespace seq { @@ -37,6 +38,10 @@ namespace seq { return true; if (reduce_itos3(e, r)) return true; + if (reduce_ubv2s1(e, r)) + return true; + if (reduce_ubv2s2(e, r)) + return true; if (reduce_binary_eq(e, r)) return true; if (reduce_nth_solved(e, r)) @@ -184,8 +189,8 @@ namespace seq { expr_ref digit = m_ax.sk().mk_digit2int(u); add_consequence(m_ax.mk_ge(digit, 1)); } - expr_ref y(seq.str.mk_concat(e.rs, e.ls[0]->get_sort()), m); - ctx.add_solution(e.ls[0], y); + expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); + ctx.add_solution(seq.str.mk_itos(n), y); return true; } @@ -209,6 +214,105 @@ namespace seq { } + + /** + * from_ubv(s) == from_ubv(t) + * -------------------------- + * s = t + */ + + bool eq_solver::match_ubv2s1(eqr const& e, expr*& a, expr*& b) { + return + e.ls.size() == 1 && e.rs.size() == 1 && + seq.str.is_ubv2s(e.ls[0], a) && seq.str.is_ubv2s(e.rs[0], b); + return false; + } + + bool eq_solver::reduce_ubv2s1(eqr const& e, eq_ptr& r) { + expr* s = nullptr, * t = nullptr; + if (!match_ubv2s1(e, s, t)) + return false; + expr_ref eq = mk_eq(s, t); + add_consequence(eq); + return true; + } + + /** + * + * bv2s(n) = [d1]+[d2]+...+[dk] + * --------------------------------- + * n = 10^{k-1}*d1 + ... + dk + * + * k > 1 => d1 > 0 + */ + bool eq_solver::match_ubv2s2(eqr const& e, expr*& n, expr_ref_vector const*& es) { + if (e.ls.size() == 1 && seq.str.is_ubv2s(e.ls[0], n)) { + es = &e.rs; + return true; + } + if (e.rs.size() == 1 && seq.str.is_ubv2s(e.rs[0], n)) { + es = &e.ls; + return true; + } + return false; + } + + bool eq_solver::reduce_ubv2s2(eqr const& e, eq_ptr& r) { + expr* n = nullptr; + expr_ref_vector const* _es = nullptr; + if (!match_ubv2s2(e, n, _es)) + return false; + + expr_ref_vector const& es = *_es; + if (es.empty()) { + set_conflict(); + return true; + } + expr* u = nullptr; + for (expr* r : es) { + if (seq.str.is_unit(r, u)) { + expr_ref is_digit = m_ax.is_digit(u); + if (!m.is_true(ctx.expr2rep(is_digit))) + add_consequence(is_digit); + } + } + if (!all_units(es, 0, es.size())) + return false; + + expr_ref num(m); + bv_util bv(m); + sort* bv_sort = n->get_sort(); + unsigned sz = bv.get_bv_size(n); + if (es.size() > (sz + log2(10)-1)/log2(10)) { + set_conflict(); + return true; + } + + for (expr* r : es) { + VERIFY(seq.str.is_unit(r, u)); + expr_ref digit = m_ax.sk().mk_digit2bv(u, bv_sort); + if (!num) + num = digit; + else + num = bv.mk_bv_add(bv.mk_bv_mul(bv.mk_numeral(10, sz), num), digit); + } + + expr_ref eq(m.mk_eq(n, num), m); + m_ax.rewrite(eq); + add_consequence(eq); + if (es.size() > 1) { + VERIFY(seq.str.is_unit(es[0], u)); + expr_ref digit = m_ax.sk().mk_digit2bv(u, bv_sort); + expr_ref eq0(m.mk_eq(digit, bv.mk_numeral(0, sz)), m); + expr_ref neq0(m.mk_not(eq0), m); + add_consequence(neq0); + } + expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); + ctx.add_solution(seq.str.mk_ubv2s(n), y); + return true; + } + + /** * Equation is of the form x ++ xs = ys ++ x * where |xs| = |ys| are units of same length diff --git a/src/ast/rewriter/seq_eq_solver.h b/src/ast/rewriter/seq_eq_solver.h index e014bb8e1..6be2531b2 100644 --- a/src/ast/rewriter/seq_eq_solver.h +++ b/src/ast/rewriter/seq_eq_solver.h @@ -64,6 +64,12 @@ namespace seq { bool reduce_itos3(eqr const& e, eq_ptr& r); bool match_itos3(eqr const& e, expr*& n, expr_ref_vector const* & es); + bool match_ubv2s1(eqr const& e, expr*& s, expr*& t); + bool reduce_ubv2s1(eqr const& e, eq_ptr& r); + + bool match_ubv2s2(eqr const& e, expr*& n, expr_ref_vector const*& es); + bool reduce_ubv2s2(eqr const& e, eq_ptr& r); + bool match_binary_eq(eqr const& e, expr_ref& x, ptr_vector& xs, ptr_vector& ys, expr_ref& y); bool reduce_binary_eq(eqr const& e, eq_ptr& r); diff --git a/src/ast/rewriter/seq_skolem.cpp b/src/ast/rewriter/seq_skolem.cpp index c76efda57..ecefe8374 100644 --- a/src/ast/rewriter/seq_skolem.cpp +++ b/src/ast/rewriter/seq_skolem.cpp @@ -201,3 +201,6 @@ expr_ref skolem::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned j, e return expr_ref(seq.mk_skolem(m_aut_step, args.size(), args.data(), m.mk_bool_sort()), m); } +expr_ref skolem::mk_digit2bv(expr* ch, sort* bv_sort) { + return mk(symbol("seq.digit2bv"), ch, nullptr, nullptr, nullptr, bv_sort); +} diff --git a/src/ast/rewriter/seq_skolem.h b/src/ast/rewriter/seq_skolem.h index 56baf8def..a7d4be917 100644 --- a/src/ast/rewriter/seq_skolem.h +++ b/src/ast/rewriter/seq_skolem.h @@ -93,6 +93,8 @@ namespace seq { expr_ref mk_is_digit(expr* ch) { return mk(symbol("seq.is_digit"), ch, nullptr, nullptr, nullptr, m.mk_bool_sort()); } expr_ref mk_unit_inv(expr* n); expr_ref mk_digit2int(expr* ch) { return mk(symbol("seq.digit2int"), ch, nullptr, nullptr, nullptr, a.mk_int()); } + expr_ref mk_digit2bv(expr* ch, sort* bv_sort); + expr_ref mk_ubv2ch(expr* b) { return mk(symbol("seq.ubv2ch"), b, nullptr, nullptr, nullptr, seq.mk_char_sort()); } expr_ref mk_left(expr* x, expr* y, expr* z = nullptr) { return mk("seq.left", x, y, z); } expr_ref mk_right(expr* x, expr* y, expr* z = nullptr) { return mk("seq.right", x, y, z); } expr_ref mk_max_unfolding_depth(unsigned d); diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index afb823284..61bbf7ba1 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -296,6 +296,7 @@ public: app* mk_char_bit(expr* e, unsigned i); app* mk_itos(expr* i) const { return m.mk_app(m_fid, OP_STRING_ITOS, 1, &i); } app* mk_stoi(expr* s) const { return m.mk_app(m_fid, OP_STRING_STOI, 1, &s); } + app* mk_ubv2s(expr* b) const { return m.mk_app(m_fid, OP_STRING_UBVTOS, 1, &b); } app* mk_is_empty(expr* s) const; app* mk_lex_lt(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_STRING_LT, 2, es); } app* mk_lex_le(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_STRING_LE, 2, es); } diff --git a/src/smt/seq_axioms.h b/src/smt/seq_axioms.h index e1dd99b64..cd7f38064 100644 --- a/src/smt/seq_axioms.h +++ b/src/smt/seq_axioms.h @@ -79,6 +79,7 @@ namespace smt { void add_stoi_axiom(expr* n) { m_ax.stoi_axiom(n); } void add_stoi_axiom(expr* e, unsigned k) { m_ax.stoi_axiom(e, k); } void add_itos_axiom(expr* s, unsigned k) { m_ax.itos_axiom(s, k); } + void add_ubv2s_axiom(expr* b, unsigned k) { m_ax.ubv2s_axiom(b, k); } void add_lt_axiom(expr* n) { m_ax.lt_axiom(n); } void add_le_axiom(expr* n) { m_ax.le_axiom(n); } void add_is_digit_axiom(expr* n) { m_ax.is_digit_axiom(n); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index e4eec609f..7e4eda114 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -262,6 +262,7 @@ theory_seq::theory_seq(context& ctx): m_axioms(m), m_axioms_head(0), m_int_string(m), + m_ubv_string(m), m_length(m), m_length_limit(m), m_mg(nullptr), @@ -368,6 +369,11 @@ final_check_status theory_seq::final_check_eh() { TRACEFIN("int_string"); return FC_CONTINUE; } + if (check_ubv_string()) { + ++m_stats.m_ubv_string; + TRACEFIN("ubv_string"); + return FC_CONTINUE; + } if (reduce_length_eq()) { ++m_stats.m_branch_variable; TRACEFIN("reduce_length"); @@ -1525,6 +1531,60 @@ bool theory_seq::add_length_to_eqc(expr* e) { return change; } +void theory_seq::add_ubv_string(expr* e) { + m_ubv_string.push_back(e); + m_trail_stack.push(push_back_vector(m_ubv_string)); +} + +bool theory_seq::check_ubv_string() { + bool change = false; + for (expr* e : m_ubv_string) { + if (check_ubv_string(e)) + change = true; + } + return change; +} + +bool theory_seq::check_ubv_string(expr* e) { + expr* n = nullptr; + if (ctx.inconsistent()) + return true; + if (m_has_ubv_axiom.contains(e)) + return false; + expr* b = nullptr; + bv_util bv(m); + VERIFY(m_util.str.is_ubv2s(e, b)); + unsigned sz = bv.get_bv_size(b); + rational value(0); + bool all_bits_assigned = true; + for (unsigned i = 0; i < sz; ++i) { + expr_ref bit(bv.mk_bit2bool(b, i), m); + literal lit = mk_literal(bit); + switch (ctx.get_assignment(lit)) { + case l_undef: + ctx.mark_as_relevant(lit); + all_bits_assigned = false; + break; + case l_true: + value += rational::power_of_two(i); + break; + case l_false: + break; + } + } + if (!all_bits_assigned) + return true; + unsigned k = 0; + while (value >= 10) { + k++; + value = div(value, rational(10)); + } + m_has_ubv_axiom.insert(e); + m_trail_stack.push(insert_obj_trail(m_has_ubv_axiom, e)); + m_ax.add_ubv2s_axiom(b, k); + return true; +} + void theory_seq::add_int_string(expr* e) { m_int_string.push_back(e); m_trail_stack.push(push_back_vector(m_int_string)); @@ -1761,6 +1821,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq extensionality", m_stats.m_extensionality); st.update("seq fixed length", m_stats.m_fixed_length); st.update("seq int.to.str", m_stats.m_int_string); + st.update("seq str.from_ubv", m_stats.m_ubv_string); } void theory_seq::init_search_eh() { @@ -3099,6 +3160,9 @@ void theory_seq::relevant_eh(app* n) { add_int_string(n); } + if (m_util.str.is_ubv2s(n)) + add_ubv_string(n); + expr* arg = nullptr; if (m_sk.is_tail(n, arg)) { add_length_limit(arg, m_max_unfolding_depth, true); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index b7ea9517a..ad6ef0695 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -323,6 +323,7 @@ namespace smt { unsigned m_fixed_length; unsigned m_propagate_contains; unsigned m_int_string; + unsigned m_ubv_string; }; typedef hashtable rational_set; @@ -348,6 +349,8 @@ namespace smt { unsigned m_axioms_head; // index of first axiom to add. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. expr_ref_vector m_int_string; + expr_ref_vector m_ubv_string; // list all occurrences of str.from_ubv that have been seen + obj_hashtable m_has_ubv_axiom; // keep track of ubv that have been axiomatized within scope. obj_hashtable m_has_length; // is length applied expr_ref_vector m_length; // length applications themselves obj_map m_length_limit_map; // sequences that have length limit predicates @@ -569,6 +572,11 @@ namespace smt { bool branch_itos(); bool branch_itos(expr* e); + // functions that convert ubv to string + void add_ubv_string(expr* e); + bool check_ubv_string(); + bool check_ubv_string(expr* e); + expr_ref add_elim_string_axiom(expr* n); void add_in_re_axiom(expr* n); literal mk_simplified_literal(expr* n);