3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-29 20:05:51 +00:00

fixes and more porting seq_eq_solver to self-contained module

This commit is contained in:
Nikolaj Bjorner 2021-03-04 16:23:22 -08:00
parent 847724fb21
commit 38737db802
21 changed files with 354 additions and 230 deletions

View file

@ -8,7 +8,7 @@ Module Name:
Abstract:
Solver-agnostic equality solving routines for sequences
Author:
Nikolaj Bjorner (nbjorner) 2021-03-01
@ -35,9 +35,25 @@ namespace seq {
return true;
if (reduce_itos2(e, r))
return true;
if (reduce_itos3(e, r))
return true;
if (reduce_binary_eq(e, r))
return true;
if (reduce_nth_solved(e, r))
return true;
return false;
}
bool eq_solver::branch(unsigned priority, eqr const& e) {
switch (priority) {
case 0:
return branch_unit_variable(e);
case 1:
default:
break;
}
return false;
}
@ -64,15 +80,15 @@ namespace seq {
* --------------------------
* s = t or (s < 0 and t < 0)
*/
bool eq_solver::match_itos1(eqr const& e, expr*& a, expr*& b) {
return
e.ls.size() == 1 && e.rs.size() == 1 &&
return
e.ls.size() == 1 && e.rs.size() == 1 &&
seq.str.is_itos(e.ls[0], a) && seq.str.is_itos(e.rs[0], b);
}
bool eq_solver::reduce_itos1(eqr const& e, eq_ptr& r) {
expr* s = nullptr, *t = nullptr;
expr* s = nullptr, * t = nullptr;
if (!match_itos1(e, s, t))
return false;
expr_ref eq = mk_eq(s, t);
@ -86,7 +102,7 @@ namespace seq {
* -----------------
* s < 0
*/
bool eq_solver::match_itos2(eqr const& e, expr*& s) {
if (e.ls.size() == 1 && e.rs.empty() && seq.str.is_itos(e.ls[0], s))
return true;
@ -103,11 +119,78 @@ namespace seq {
return true;
}
/**
* itos(n) = ""
* ------------
* n <= -1
*
* itos(n) = [d1]+[d2]+...+[dk]
* ---------------------------------
* n = 10^{k-1}*d1 + ... + dk
*
* k > 1 => d1 > 0
*/
bool eq_solver::match_itos3(eqr const& e, expr*& n, expr_ref_vector const*& es) {
if (e.ls.size() == 1 && seq.str.is_itos(e.ls[0], n)) {
es = &e.rs;
return true;
}
if (e.rs.size() == 1 && seq.str.is_itos(e.rs[0], n)) {
es = &e.ls;
return true;
}
return false;
}
bool eq_solver::reduce_itos3(eqr const& e, eq_ptr& r) {
expr* n = nullptr;
expr_ref_vector const* _es = nullptr;
if (!match_itos3(e, n, _es))
return false;
expr_ref_vector const& es = *_es;
if (es.empty()) {
add_consequence(m_ax.mk_le(n, -1));
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);
for (expr* r : es) {
VERIFY(seq.str.is_unit(r, u));
expr_ref digit = m_ax.sk().mk_digit2int(u);
if (!num)
num = digit;
else
num = a.mk_add(a.mk_mul(a.mk_int(10), 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_digit2int(u);
add_consequence(m_ax.mk_ge(digit, 1));
}
return true;
}
/**
* x = t, where x does not occur in t.
*/
bool eq_solver::reduce_unit(eqr const& e, eq_ptr& r) {
if (e.ls == e.rs)
if (e.ls == e.rs)
return true;
if (e.ls.size() == 1 && is_var(e.ls[0]) && !occurs(e.ls[0], e.rs)) {
expr_ref y(seq.str.mk_concat(e.rs, e.ls[0]->get_sort()), m);
@ -145,7 +228,7 @@ namespace seq {
set_conflict();
return true;
}
if (xs.empty())
if (xs.empty())
return true;
if (xs.size() != 1)
@ -155,29 +238,105 @@ namespace seq {
return false;
expr_ref eq(m.mk_eq(xs[0], ys[0]), m);
expr* veq = ctx.expr2rep(eq);
if (m.is_true(veq))
if (m.is_true(veq))
return false;
add_consequence(eq);
return m.is_false(veq);
}
/**
* x = nth(unit(x, 0)) ++ ... ++ nth(unit(x,i))
* ------------------------------------------------
* x -> nth(unit(x, 0)) ++ ... ++ nth(unit(x,i))
*/
bool eq_solver::reduce_nth_solved(eqr const& e, eq_ptr& r) {
expr_ref x(m), y(m);
if (match_nth_solved(e, x, y)) {
ctx.add_solution(x, y);
return true;
}
return false;
}
bool eq_solver::match_nth_solved(eqr const& e, expr_ref& x, expr_ref& y) {
if (match_nth_solved_aux(e.ls, e.rs, x, y))
return true;
if (match_nth_solved_aux(e.rs, e.ls, x, y))
return true;
return false;
}
bool eq_solver::match_nth_solved_aux(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref& x, expr_ref& y) {
if (ls.size() != 1 || !is_var(ls[0]))
return false;
expr* n = nullptr, * u = nullptr;
unsigned j = 0, i = 0;
for (expr* r : rs) {
if (seq.str.is_unit(r, u) && seq.str.is_nth_i(u, n, i) && i == j && n == ls[0])
++j;
else
return false;
}
x = ls[0];
y = seq.str.mk_concat(rs, x->get_sort());
return true;
}
/**
* XV == abcdef, where V is an arbitrary string
* ---------------------------------------------
* |X| <= |abcdef|
*
* |X| = k => X = a_1...a_k
*/
bool eq_solver::branch_unit_variable(expr* X, expr_ref_vector const& units) {
SASSERT(is_var(X));
rational lenX;
ctx.get_length(X, lenX);
if (lenX > units.size()) {
add_consequence(m_ax.mk_le(seq.str.mk_length(X), units.size()));
return true;
}
expr_ref eq_length(m.mk_eq(a.mk_int(lenX), seq.str.mk_length(X)), m);
expr* val = ctx.expr2rep(eq_length);
if (!m.is_false(val)) {
expr_ref Y(seq.str.mk_concat(lenX.get_unsigned(), units.c_ptr(), X->get_sort()), m);
expr_ref eq = m_ax.sk().mk_eq(X, Y);
add_consequence(~eq_length, eq);
return true;
}
return false;
}
bool eq_solver::branch_unit_variable(eqr const& e) {
if (!e.ls.empty() && is_var(e.ls[0]) && all_units(e.rs, 0, e.rs.size()))
return branch_unit_variable(e.ls[0], e.rs);
if (!e.rs.empty() && is_var(e.rs[0]) && all_units(e.ls, 0, e.ls.size()))
return branch_unit_variable(e.rs[0], e.ls);
return false;
}
bool eq_solver::is_var(expr* a) const {
return
seq.is_seq(a) &&
!seq.str.is_concat(a) &&
!seq.str.is_empty(a) &&
!seq.str.is_empty(a) &&
!seq.str.is_string(a) &&
!seq.str.is_unit(a) &&
!seq.str.is_itos(a) &&
!seq.str.is_nth_i(a) &&
!seq.str.is_nth_i(a) &&
!m.is_ite(a);
}
bool eq_solver::occurs(expr* a, expr_ref_vector const& b) {
for (auto const& elem : b)
if (a == elem || m.is_ite(elem))
return true;
for (auto const& elem : b)
if (a == elem || m.is_ite(elem))
return true;
return false;
}
@ -185,7 +344,7 @@ namespace seq {
// true if a occurs under an interpreted function or under left/right selector.
SASSERT(is_var(a));
SASSERT(m_todo.empty());
expr* e1 = nullptr, *e2 = nullptr;
expr* e1 = nullptr, * e2 = nullptr;
m_todo.push_back(b);
while (!m_todo.empty()) {
b = m_todo.back();
@ -220,8 +379,8 @@ namespace seq {
unsigned eq_solver::count_units_l2r(expr_ref_vector const& es, unsigned offset) const {
unsigned i = offset, sz = es.size();
for (; i < sz && seq.str.is_unit(es[i]); ++i) ;
return i - offset;
for (; i < sz && seq.str.is_unit(es[i]); ++i);
return i - offset;
}
unsigned eq_solver::count_units_r2l(expr_ref_vector const& es, unsigned offset) const {
@ -231,15 +390,14 @@ namespace seq {
if (!seq.str.is_unit(es[i]))
break;
++count;
}
while (i-- > 0);
} while (i-- > 0);
return count;
}
unsigned eq_solver::count_non_units_l2r(expr_ref_vector const& es, unsigned offset) const {
unsigned i = offset, sz = es.size();
for (; i < sz && !seq.str.is_unit(es[i]); ++i) ;
return i - offset;
for (; i < sz && !seq.str.is_unit(es[i]); ++i);
return i - offset;
}
unsigned eq_solver::count_non_units_r2l(expr_ref_vector const& es, unsigned offset) const {
@ -249,8 +407,7 @@ namespace seq {
if (seq.str.is_unit(es[i]))
break;
++count;
}
while (i-- > 0);
} while (i-- > 0);
return count;
}
@ -259,9 +416,9 @@ namespace seq {
* where U is a variable or concatenation of variables
*/
bool eq_solver::match_ternary_eq_r(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
bool eq_solver::match_ternary_eq_r(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
unsigned num_ls_units = count_units_r2l(ls, ls.size() - 1);
if (num_ls_units == 0 || num_ls_units == ls.size())
return false;
@ -273,33 +430,33 @@ namespace seq {
unsigned num_rs_units = count_units_r2l(rs, rs.size() - 1 - num_rs_non_units);
if (num_rs_units == 0)
return false;
set_prefix(x, ls, ls.size() - num_ls_units);
set_suffix(xs, ls, num_ls_units);
set_prefix(x, ls, ls.size() - num_ls_units);
set_suffix(xs, ls, num_ls_units);
unsigned offset = rs.size() - num_rs_non_units - num_rs_units;
set_prefix(y1, rs, offset);
set_prefix(y1, rs, offset);
set_extract(ys, rs, offset, num_rs_units);
set_suffix(y2, rs, num_rs_non_units);
set_suffix(y2, rs, num_rs_non_units);
return true;
}
return false;
}
bool eq_solver::match_ternary_eq_rhs(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
bool eq_solver::match_ternary_eq_rhs(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x, expr_ref_vector& xs, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
if (match_ternary_eq_r(ls, rs, x, xs, y1, ys, y2))
return true;
if (match_ternary_eq_r(rs, ls, x, xs, y1, ys, y2))
return true;
return false;
}
/*
match: abc X .. = Y def Z ..
where Y is a variable or concatenation of variables
*/
bool eq_solver::match_ternary_eq_l(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
bool eq_solver::match_ternary_eq_l(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
if (ls.size() > 1 && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
unsigned num_ls_units = count_units_l2r(ls, 0);
if (num_ls_units == 0 || num_ls_units == ls.size())
@ -310,18 +467,18 @@ namespace seq {
unsigned num_rs_units = count_units_l2r(rs, num_rs_non_units);
if (num_rs_units == 0)
return false;
set_prefix(xs, ls, num_ls_units);
set_suffix(x, ls, ls.size() - num_ls_units);
set_prefix(y1, rs, num_rs_non_units);
set_prefix(xs, ls, num_ls_units);
set_suffix(x, ls, ls.size() - num_ls_units);
set_prefix(y1, rs, num_rs_non_units);
set_extract(ys, rs, num_rs_non_units, num_rs_units);
set_suffix(y2, rs, rs.size() - num_rs_non_units - num_rs_units);
set_suffix(y2, rs, rs.size() - num_rs_non_units - num_rs_units);
return true;
}
return false;
}
bool eq_solver::match_ternary_eq_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
bool eq_solver::match_ternary_eq_lhs(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref_vector& xs, expr_ref& x, expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
if (match_ternary_eq_l(ls, rs, xs, x, y1, ys, y2))
return true;
if (match_ternary_eq_l(rs, ls, xs, x, y1, ys, y2))
@ -331,8 +488,8 @@ namespace seq {
bool eq_solver::all_units(expr_ref_vector const& es, unsigned start, unsigned end) const {
for (unsigned i = start; i < end; ++i)
if (!seq.str.is_unit(es[i]))
for (unsigned i = start; i < end; ++i)
if (!seq.str.is_unit(es[i]))
return false;
return true;
}
@ -340,9 +497,9 @@ namespace seq {
/**
match X abc = defg Y, for abc, defg non-empty
*/
bool eq_solver::match_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y) {
bool eq_solver::match_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x, ptr_vector<expr>& xs, ptr_vector<expr>& ys, expr_ref& y) {
if (ls.size() > 1 && is_var(ls[0]) &&
rs.size() > 1 && is_var(rs.back()) &&
all_units(ls, 1, ls.size()) &&
@ -369,8 +526,8 @@ namespace seq {
*/
bool eq_solver::match_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs,
expr_ref& x1, expr_ref_vector& xs, expr_ref& x2,
expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
expr_ref& x1, expr_ref_vector& xs, expr_ref& x2,
expr_ref& y1, expr_ref_vector& ys, expr_ref& y2) {
if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) &&
rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) {
unsigned ls_non_unit = count_non_units_l2r(ls, 0);
@ -385,18 +542,78 @@ namespace seq {
return false;
if (rs_unit == 0)
return false;
set_prefix(x1, ls, ls_non_unit);
set_prefix(x1, ls, ls_non_unit);
set_extract(xs, ls, ls_non_unit, ls_unit);
set_suffix(x2, ls, ls.size() - ls_non_unit - ls_unit);
set_prefix(y1, rs, rs_non_unit);
set_suffix(x2, ls, ls.size() - ls_non_unit - ls_unit);
set_prefix(y1, rs, rs_non_unit);
set_extract(ys, rs, rs_non_unit, rs_unit);
set_suffix(y2, rs, rs.size() - rs_non_unit - rs_unit);
set_suffix(y2, rs, rs.size() - rs_non_unit - rs_unit);
return true;
}
return false;
}
// exists x, y, rs' != empty s.t. (ls = x ++ rs ++ y) || (ls = rs' ++ y && rs = x ++ rs')
bool eq_solver::can_align_from_lhs_aux(expr_ref_vector const& ls, expr_ref_vector const& rs) {
SASSERT(!ls.empty() && !rs.empty());
for (unsigned i = 0; i < ls.size(); ++i) {
if (m.are_distinct(ls[i], rs.back()))
continue;
if (i == 0)
return true;
// ls = rs' ++ y && rs = x ++ rs', diff = |x|
bool same = true;
if (rs.size() > i) {
unsigned diff = rs.size() - (i + 1);
for (unsigned j = 0; same && j < i; ++j)
same = !m.are_distinct(ls[j], rs[diff + j]);
}
// ls = x ++ rs ++ y, diff = |x|
else {
unsigned diff = (i + 1) - rs.size();
for (unsigned j = 0; same && j + 1 < rs.size(); ++j)
same = !m.are_distinct(ls[diff + j], rs[j]);
}
if (same)
return true;
}
return false;
}
// exists x, y, rs' != empty s.t. (ls = x ++ rs ++ y) || (ls = x ++ rs' && rs = rs' ++ y)
bool eq_solver::can_align_from_rhs_aux(expr_ref_vector const& ls, expr_ref_vector const& rs) {
SASSERT(!ls.empty() && !rs.empty());
for (unsigned i = 0; i < ls.size(); ++i) {
unsigned diff = ls.size() - 1 - i;
if (m.are_distinct(ls[diff], rs[0]))
continue;
if (i == 0)
return true;
bool same = true;
// ls = x ++ rs' && rs = rs' ++ y, diff = |x|
if (rs.size() > i) {
for (unsigned j = 1; same && j <= i; ++j)
same = !m.are_distinct(ls[diff + j], rs[j]);
}
// ls = x ++ rs ++ y, diff = |x|
else {
for (unsigned j = 1; same && j < rs.size(); ++j)
same = !m.are_distinct(ls[diff + j], rs[j]);
}
if (same)
return true;
}
return false;
}
};