3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-18 02:16:40 +00:00

tune axioms for derivatives

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2020-05-25 12:41:30 -07:00
parent 97b480ded3
commit 4e01d5b5c1
8 changed files with 207 additions and 65 deletions

View file

@ -31,12 +31,9 @@ namespace smt {
class seq_util::str& seq_regex::str() { return th.m_util.str; }
seq_rewriter& seq_regex::seq_rw() { return th.m_seq_rewrite; }
seq_skolem& seq_regex::sk() { return th.m_sk; }
arith_util& seq_regex::a() { return th.m_autil; }
void seq_regex::rewrite(expr_ref& e) { th.m_rewrite(e); }
void seq_regex::propagate_in_re(literal lit) {
if (!propagate(lit))
m_to_propagate.push_back(lit);
}
bool seq_regex::propagate() {
bool change = false;
@ -57,24 +54,16 @@ namespace smt {
* (not (str.in.re s r)) => (str.in.re s (complement r))
* (str.in.re s r) => r != {}
*
* (str.in.re s r[if(c,r1,r2)]) & c => (str.in.re s r[r1])
* (str.in.re s r[if(c,r1,r2)]) & ~c => (str.in.re s r[r2])
* (str.in.re s r) & s = "" => nullable(r)
* s != "" => s = unit(head) ++ tail
* (str.in.re s r) & s != "" => (str.in.re tail D(head,r))
* (str.in.re s r) => (accept s 0 r)
*/
bool seq_regex::propagate(literal lit) {
void seq_regex::propagate_in_re(literal lit) {
expr* s = nullptr, *r = nullptr;
expr* e = ctx.bool_var2expr(lit.var());
VERIFY(str().is_in_re(e, s, r));
TRACE("seq", tout << "propagate " << mk_pp(e, m) << "\n";);
// only positive assignments of membership propagation are relevant.
if (lit.sign() && sk().is_tail(s))
return true;
// convert negative negative membership literals to positive
// ~(s in R) => s in C(R)
if (lit.sign()) {
@ -82,43 +71,99 @@ namespace smt {
rewrite(fml);
literal nlit = th.mk_literal(fml);
th.propagate_lit(nullptr, 1, &lit, nlit);
return true;
return;
}
if (!sk().is_tail(s) && coallesce_in_re(lit))
return true;
if (coallesce_in_re(lit))
return;
// TBD
// for !sk().is_tail(s): s in R => R != {}
if (false && !sk().is_tail(s)) {
// TBD s in R => R != {}
if (false) {
expr_ref is_empty(m.mk_eq(r, re().mk_empty(m.get_sort(s))), m);
rewrite(is_empty);
literal is_emptyl = th.mk_literal(is_empty);
if (ctx.get_assignment(is_emptyl) != l_false) {
th.propagate_lit(nullptr, 1, &lit, ~is_emptyl);
return true;
return;
}
}
if (block_unfolding(lit, s))
expr_ref zero(a().mk_int(0), m);
expr_ref acc = sk().mk_accept(s, zero, r);
literal acc_lit = th.mk_literal(acc);
th.propagate_lit(nullptr, 1, &lit, acc_lit);
}
void seq_regex::propagate_accept(literal lit) {
if (!propagate(lit))
m_to_propagate.push_back(lit);
}
/**
* Propagate the atom (accept s i r)
*
* Propagation implements the following inference rules
*
* (accept s i r[if(c,r1,r2)]) & c => (accept s i r[r1])
* (accept s i r[if(c,r1,r2)]) & ~c => (accept s i r[r2])
* (accept s i r) & len(s) <= i => nullable(r)
* (accept s r) & len(s) > i => (accept s (+ i 1) D(nth(s,i), r))
*/
bool seq_regex::propagate(literal lit) {
SASSERT(!lit.sign());
expr* s = nullptr, *i = nullptr, *r = nullptr;
expr* e = ctx.bool_var2expr(lit.var());
VERIFY(sk().is_accept(e, s, i, r));
unsigned idx = 0;
rational n;
VERIFY(a().is_numeral(i, n));
SASSERT(n.is_unsigned());
idx = n.get_unsigned();
TRACE("seq", tout << "propagate " << mk_pp(e, m) << "\n";);
if (block_unfolding(lit, idx))
return true;
// s in R & len(s) <= i => nullable(R)
literal len_s_le_i = th.m_ax.mk_le(th.mk_len(s), idx);
switch (ctx.get_assignment(len_s_le_i)) {
case l_undef:
ctx.mark_as_relevant(len_s_le_i);
return false;
case l_true: {
expr_ref is_nullable = seq_rw().is_nullable(r);
rewrite(is_nullable);
th.add_axiom(~lit, ~len_s_le_i, th.mk_literal(is_nullable));
return true;
}
case l_false:
break;
}
// s in R[if(p,R1,R2)] & p => s in R[R1]
// s in R[if(p,R1,R2)] & ~p => s in R[R2]
// TBD combine the two places that perform co-factor rewriting
expr_ref cond(m), tt(m), el(m);
if (seq_rw().has_cofactor(r, cond, tt, el)) {
rewrite(cond);
literal lcond = th.mk_literal(cond), next_lit;
switch (ctx.get_assignment(lcond)) {
case l_true: {
rewrite(tt);
literal lits[2] = { lit, lcond };
next_lit = th.mk_literal(re().mk_in_re(s, tt));
next_lit = th.mk_literal(sk().mk_accept(s, i, tt));
th.propagate_lit(nullptr, 2, lits, next_lit);
return true;
}
case l_false: {
rewrite(el);
next_lit = th.mk_literal(re().mk_in_re(s, el));
next_lit = th.mk_literal(sk().mk_accept(s, i, el));
literal lits[2] = { lit, ~lcond };
th.propagate_lit(nullptr, 2, lits, next_lit);
return true;
@ -129,34 +174,51 @@ namespace smt {
}
}
// s in R & s = "" => nullable(R)
literal is_empty = th.mk_eq(s, str().mk_empty(m.get_sort(s)), false);
expr_ref is_nullable = seq_rw().is_nullable(r);
rewrite(is_nullable);
th.add_axiom(~lit, ~is_empty, th.mk_literal(is_nullable));
switch (ctx.get_assignment(is_empty)) {
case l_undef:
ctx.mark_as_relevant(is_empty);
return false;
case l_true:
return true;
case l_false:
break;
}
// s in R & s != "" => s = head ++ tail
// s in R & s != "" => tail in D(head, R)
expr_ref head(m), tail(m), d(m);
expr* h = nullptr;
sk().decompose(s, head, tail);
if (!str().is_unit(head, h))
throw default_exception("expected a unit");
d = seq_rw().derivative(h, r);
// s in R & len(s) > i => tail(s,i) in D(nth(s, i), R)
expr_ref head(m), d(m), i_next(m);
head = th.mk_nth(s, i);
i_next = a().mk_int(idx + 1);
d = seq_rw().derivative(head, r);
if (!d)
throw default_exception("unable to expand derivative");
th.add_axiom(is_empty, th.mk_eq(s, th.mk_concat(head, tail), false));
th.add_axiom(~lit, is_empty, th.mk_literal(re().mk_in_re(tail, d)));
// immediately expand a co-factor here so that condition on the character
// is expanded.
literal_vector conds;
while (seq_rw().has_cofactor(d, cond, tt, el)) {
rewrite(cond);
literal lcond = th.mk_literal(cond);
bool is_undef = false;
switch (ctx.get_assignment(lcond)) {
case l_true:
conds.push_back(~lcond);
d = tt;
break;
case l_false:
conds.push_back(lcond);
d = el;
break;
case l_undef:
ctx.mark_as_relevant(lcond);
d = m.mk_ite(cond, tt, el);
is_undef = true;
break;
}
if (is_undef)
break;
rewrite(d);
}
if (conds.empty()) {
th.add_axiom(~lit, len_s_le_i, th.mk_literal(sk().mk_accept(s, i_next, d)));
}
else {
conds.push_back(~lit);
conds.push_back(len_s_le_i);
conds.push_back(th.mk_literal(sk().mk_accept(s, i_next, d)));
for (literal lit : conds) ctx.mark_as_relevant(lit);
ctx.mk_th_axiom(th.get_id(), conds.size(), conds.c_ptr());
}
TRACE("seq", tout << "head " << head << "\n";
tout << mk_pp(r, m) << "\n";);
return true;
@ -172,11 +234,8 @@ namespace smt {
* Limiting the depth of unfolding s below such lengths is not useful.
*/
bool seq_regex::block_unfolding(literal lit, expr* s) {
bool seq_regex::block_unfolding(literal lit, unsigned i) {
expr* t = nullptr;
unsigned i = 0;
if (!sk().is_tail_u(s, t, i))
return false;
if (i > th.m_max_unfolding_depth &&
th.m_max_unfolding_lit != null_literal &&
ctx.get_assignment(th.m_max_unfolding_lit) == l_true &&