3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-27 08:28:44 +00:00

fixups to theory_str indexof and axiom handling loop

This commit is contained in:
Murphy Berzish 2018-03-19 18:06:42 -04:00
parent 84c30e0b60
commit 5c692dc79d

View file

@ -638,6 +638,7 @@ namespace smt {
return contains; return contains;
} }
// note, this invokes "special-case" handling for the start index being 0
app * theory_str::mk_indexof(expr * haystack, expr * needle) { app * theory_str::mk_indexof(expr * haystack, expr * needle) {
app * indexof = u.str.mk_index(haystack, needle, mk_int(0)); app * indexof = u.str.mk_index(haystack, needle, mk_int(0));
m_trail.push_back(indexof); m_trail.push_back(indexof);
@ -823,7 +824,17 @@ namespace smt {
} }
m_concat_eval_todo.reset(); m_concat_eval_todo.reset();
for (enode * e : m_library_aware_axiom_todo) { while(true) {
// Special handling: terms can recursively set up other terms
// (e.g. indexof can instantiate other indexof terms).
// - Copy the list so it can potentially be modified during setup.
// - Don't clear this list if new ones are added in the process;
// instead, set up all the new terms before proceeding.
// TODO see if any other propagate() worklists need this kind of handling
// TODO we really only need to check the new ones on each pass
unsigned start_count = m_library_aware_axiom_todo.size();
ptr_vector<enode> axioms_tmp(m_library_aware_axiom_todo);
for (auto const& e : axioms_tmp) {
app * a = e->get_owner(); app * a = e->get_owner();
if (u.str.is_stoi(a)) { if (u.str.is_stoi(a)) {
instantiate_axiom_str_to_int(e); instantiate_axiom_str_to_int(e);
@ -850,6 +861,14 @@ namespace smt {
NOT_IMPLEMENTED_YET(); NOT_IMPLEMENTED_YET();
} }
} }
unsigned end_count = m_library_aware_axiom_todo.size();
if (end_count > start_count) {
TRACE("str", tout << "new library-aware terms added during axiom setup -- checking again" << std::endl;);
continue;
} else {
break;
}
}
m_library_aware_axiom_todo.reset(); m_library_aware_axiom_todo.reset();
for (auto el : m_delayed_axiom_setup_terms) { for (auto el : m_delayed_axiom_setup_terms) {
@ -1313,74 +1332,85 @@ namespace smt {
} }
} }
void theory_str::instantiate_axiom_Indexof_extended(enode * e) { void theory_str::instantiate_axiom_Indexof_extended(enode * _e) {
context & ctx = get_context(); context & ctx = get_context();
ast_manager & m = get_manager(); ast_manager & m = get_manager();
app * expr = e->get_owner(); app * e = _e->get_owner();
if (axiomatized_terms.contains(expr)) { if (axiomatized_terms.contains(e)) {
TRACE("str", tout << "already set up extended str.indexof axiom for " << mk_pp(expr, m) << std::endl;); TRACE("str", tout << "already set up extended str.indexof axiom for " << mk_pp(e, m) << std::endl;);
return; return;
} }
SASSERT(expr->get_num_args() == 3); SASSERT(e->get_num_args() == 3);
axiomatized_terms.insert(expr); axiomatized_terms.insert(e);
TRACE("str", tout << "instantiate extended str.indexof axiom for " << mk_pp(expr, m) << std::endl;); TRACE("str", tout << "instantiate extended str.indexof axiom for " << mk_pp(e, m) << std::endl;);
// ------------------------------------------------------------------------------- // str.indexof(H, N, i):
// if (arg[2] >= length(arg[0])) // ite2 // i < 0 --> -1
// resAst = -1 // i == 0 --> str.indexof(H, N, 0)
// else // i >= len(H) --> -1
// args[0] = prefix . suffix // 0 < i < len(H) -->
// /\ indexAst = indexof(suffix, arg[1]) // H = hd ++ tl
// /\ args[2] = len(prefix) // len(hd) = i
// /\ if (indexAst == -1) resAst = indexAst // ite3 // str.indexof(tl, N, 0)
// else resAst = args[2] + indexAst
// -------------------------------------------------------------------------------
expr_ref resAst(mk_int_var("res"), m); expr * H; // "haystack"
expr_ref indexAst(mk_int_var("index"), m); expr * N; // "needle"
expr_ref prefix(mk_str_var("prefix"), m); expr * i; // start index
expr_ref suffix(mk_str_var("suffix"), m); u.str.is_index(e, H, N, i);
expr_ref prefixLen(mk_strlen(prefix), m);
expr_ref zeroAst(mk_int(0), m);
expr_ref negOneAst(mk_int(-1), m);
expr_ref ite3(m.mk_ite( expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
ctx.mk_eq_atom(indexAst, negOneAst), expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
ctx.mk_eq_atom(resAst, negOneAst),
ctx.mk_eq_atom(resAst, m_autil.mk_add(expr->get_arg(2), indexAst))
),m);
expr_ref_vector ite2ElseItems(m); // case split
ite2ElseItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(prefix, suffix)));
ite2ElseItems.push_back(ctx.mk_eq_atom(indexAst, mk_indexof(suffix, expr->get_arg(1))));
ite2ElseItems.push_back(ctx.mk_eq_atom(expr->get_arg(2), prefixLen));
ite2ElseItems.push_back(ite3);
expr_ref ite2Else(mk_and(ite2ElseItems), m);
SASSERT(ite2Else);
expr_ref ite2(m.mk_ite( // case 1: i < 0
//m_autil.mk_ge(expr->get_arg(2), mk_strlen(expr->get_arg(0))), {
m_autil.mk_ge(m_autil.mk_add(expr->get_arg(2), m_autil.mk_mul(mk_int(-1), mk_strlen(expr->get_arg(0)))), zeroAst), expr_ref premise(m_autil.mk_le(i, minus_one), m);
ctx.mk_eq_atom(resAst, negOneAst), expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m);
ite2Else assert_implication(premise, conclusion);
), m); }
SASSERT(ite2);
expr_ref ite1(m.mk_ite( // case 2: i = 0
//m_autil.mk_lt(expr->get_arg(2), zeroAst), {
mk_not(m, m_autil.mk_ge(expr->get_arg(2), zeroAst)), expr_ref premise(ctx.mk_eq_atom(i, zero), m);
ctx.mk_eq_atom(resAst, mk_indexof(expr->get_arg(0), expr->get_arg(1))), // reduction to simpler case
ite2 expr_ref conclusion(ctx.mk_eq_atom(e, mk_indexof(H, N)), m);
), m); assert_implication(premise, conclusion);
SASSERT(ite1); }
assert_axiom(ite1); // case 3: i >= len(H)
{
//expr_ref _premise(m_autil.mk_ge(i, mk_strlen(H)), m);
//expr_ref premise(_premise);
//th_rewriter rw(m);
//rw(premise);
expr_ref premise(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero), m);
expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m);
assert_implication(premise, conclusion);
}
// case 4: 0 < i < len(H)
{
expr_ref premise1(m_autil.mk_gt(i, zero), m);
expr_ref premise2(m_autil.mk_lt(i, mk_strlen(H)), m);
expr_ref _premise(m.mk_and(premise1, premise2), m);
expr_ref premise(_premise);
th_rewriter rw(m);
rw(premise);
expr_ref reduceTerm(ctx.mk_eq_atom(expr, resAst), m); expr_ref hd(mk_str_var("hd"), m);
SASSERT(reduceTerm); expr_ref tl(mk_str_var("tl"), m);
assert_axiom(reduceTerm);
expr_ref_vector conclusion_terms(m);
conclusion_terms.push_back(ctx.mk_eq_atom(H, mk_concat(hd, tl)));
conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(hd), i));
conclusion_terms.push_back(ctx.mk_eq_atom(e, mk_indexof(tl, N)));
expr_ref conclusion(mk_and(conclusion_terms), m);
assert_implication(premise, conclusion);
}
/*
{ {
// heuristic: integrate with str.contains information // heuristic: integrate with str.contains information
// (but don't introduce it if it isn't already in the instance) // (but don't introduce it if it isn't already in the instance)
@ -1394,6 +1424,7 @@ namespace smt {
// we can't assert this during init_search as it breaks an invariant if the instance becomes inconsistent // we can't assert this during init_search as it breaks an invariant if the instance becomes inconsistent
m_delayed_axiom_setup_terms.push_back(containsAxiom); m_delayed_axiom_setup_terms.push_back(containsAxiom);
} }
*/
} }
void theory_str::instantiate_axiom_LastIndexof(enode * e) { void theory_str::instantiate_axiom_LastIndexof(enode * e) {
@ -7411,8 +7442,7 @@ namespace smt {
if (is_app(ex)) { if (is_app(ex)) {
app * ap = to_app(ex); app * ap = to_app(ex);
// TODO indexof2/lastindexof if (u.str.is_index(ap)) {
if (u.str.is_index(ap) /* || is_Indexof2(ap) || is_LastIndexof(ap) */) {
m_library_aware_axiom_todo.push_back(n); m_library_aware_axiom_todo.push_back(n);
} else if (u.str.is_stoi(ap)) { } else if (u.str.is_stoi(ap)) {
TRACE("str", tout << "found string-integer conversion term: " << mk_pp(ex, get_manager()) << std::endl;); TRACE("str", tout << "found string-integer conversion term: " << mk_pp(ex, get_manager()) << std::endl;);