mirror of
https://github.com/Z3Prover/z3
synced 2025-04-10 19:27:06 +00:00
split into seq_axioms and seq_skolem
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
d5eef9dd8b
commit
501aa7927d
|
@ -883,8 +883,8 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
|
|||
return mk_str_fun(k, arity, domain, range, OP_SEQ_EXTRACT);
|
||||
|
||||
case _OP_SEQ_SKOLEM: {
|
||||
if (num_parameters != 1 || !parameters[0].is_symbol()) {
|
||||
m.raise_exception("one symbol parameter expected to skolem symbol");
|
||||
if (num_parameters == 0 || !parameters[0].is_symbol()) {
|
||||
m.raise_exception("first parameter to skolem symbol should be a parameter");
|
||||
}
|
||||
symbol s = parameters[0].get_symbol();
|
||||
return m.mk_func_decl(s, arity, domain, range, func_decl_info(m_family_id, k, num_parameters, parameters));
|
||||
|
|
|
@ -12,6 +12,8 @@ z3_add_component(smt
|
|||
mam.cpp
|
||||
old_interval.cpp
|
||||
qi_queue.cpp
|
||||
seq_axioms.cpp
|
||||
seq_skolem.cpp
|
||||
smt_almost_cg_table.cpp
|
||||
smt_arith_value.cpp
|
||||
smt_case_split_queue.cpp
|
||||
|
|
596
src/smt/seq_axioms.cpp
Normal file
596
src/smt/seq_axioms.cpp
Normal file
|
@ -0,0 +1,596 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
seq_axioms.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Axiomatize string operations that can be reduced to
|
||||
more basic operations. These axioms are kept outside
|
||||
of a particular solver: they are mainly solver independent.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2020-4-16
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "smt/seq_axioms.h"
|
||||
#include "smt/smt_context.h"
|
||||
|
||||
using namespace smt;
|
||||
|
||||
seq_axioms::seq_axioms(theory& th, th_rewriter& r):
|
||||
th(th),
|
||||
m_rewrite(r),
|
||||
m(r.m()),
|
||||
a(m),
|
||||
seq(m),
|
||||
m_sk(m, r)
|
||||
{}
|
||||
|
||||
literal seq_axioms::mk_eq(expr* a, expr* b) {
|
||||
return th.mk_eq(a, b, false);
|
||||
}
|
||||
|
||||
literal seq_axioms::mk_literal(expr* _e) {
|
||||
expr_ref e(_e, m);
|
||||
if (a.is_arith_expr(e)) {
|
||||
m_rewrite(e);
|
||||
}
|
||||
th.ensure_enode(e);
|
||||
return ctx().get_literal(e);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
let e = extract(s, i, l)
|
||||
|
||||
i is start index, l is length of substring starting at index.
|
||||
|
||||
i < 0 => e = ""
|
||||
i >= |s| => e = ""
|
||||
l <= 0 => e = ""
|
||||
0 <= i < |s| & l > 0 => s = xey, |x| = i, |e| = min(l, |s|-i)
|
||||
l <= 0 => e = ""
|
||||
|
||||
this translates to:
|
||||
|
||||
0 <= i <= |s| -> s = xey
|
||||
0 <= i <= |s| -> len(x) = i
|
||||
0 <= i <= |s| & 0 <= l <= |s| - i -> |e| = l
|
||||
0 <= i <= |s| & |s| < l + i -> |e| = |s| - i
|
||||
|e| = 0 <=> i < 0 | |s| <= i | l <= 0 | |s| <= 0
|
||||
|
||||
It follows that:
|
||||
|e| = min(l, |s| - i) for 0 <= i < |s| and 0 < |l|
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
void seq_axioms::add_extract_axiom(expr* e) {
|
||||
TRACE("seq", tout << mk_pp(e, m) << "\n";);
|
||||
expr* s = nullptr, *i = nullptr, *l = nullptr;
|
||||
VERIFY(seq.str.is_extract(e, s, i, l));
|
||||
if (is_tail(s, i, l)) {
|
||||
add_tail_axiom(e, s);
|
||||
return;
|
||||
}
|
||||
if (is_drop_last(s, i, l)) {
|
||||
add_drop_last_axiom(e, s);
|
||||
return;
|
||||
}
|
||||
if (is_extract_prefix0(s, i, l)) {
|
||||
add_extract_prefix_axiom(e, s, l);
|
||||
return;
|
||||
}
|
||||
if (is_extract_suffix(s, i, l)) {
|
||||
add_extract_suffix_axiom(e, s, i);
|
||||
return;
|
||||
}
|
||||
expr_ref x = m_sk.mk_pre(s, i);
|
||||
expr_ref ls = mk_len(s);
|
||||
expr_ref lx = mk_len(x);
|
||||
expr_ref le = mk_len(e);
|
||||
expr_ref ls_minus_i_l(mk_sub(mk_sub(ls, i), l), m);
|
||||
expr_ref y = m_sk.mk_post(s, a.mk_add(i, l));
|
||||
expr_ref xe = mk_concat(x, e);
|
||||
expr_ref xey = mk_concat(x, e, y);
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
|
||||
literal i_ge_0 = mk_literal(a.mk_ge(i, zero));
|
||||
literal i_le_ls = mk_literal(a.mk_le(mk_sub(i, ls), zero));
|
||||
literal ls_le_i = mk_literal(a.mk_le(mk_sub(ls, i), zero));
|
||||
literal ls_ge_li = mk_literal(a.mk_ge(ls_minus_i_l, zero));
|
||||
literal l_ge_0 = mk_literal(a.mk_ge(l, zero));
|
||||
literal l_le_0 = mk_literal(a.mk_le(l, zero));
|
||||
literal ls_le_0 = mk_literal(a.mk_le(ls, zero));
|
||||
literal le_is_0 = mk_eq(le, zero);
|
||||
|
||||
|
||||
// 0 <= i & i <= |s| & 0 <= l => xey = s
|
||||
// 0 <= i & i <= |s| => |x| = i
|
||||
// 0 <= i & i <= |s| & l >= 0 & |s| >= l + i => |e| = l
|
||||
// 0 <= i & i <= |s| & |s| < l + i => |e| = |s| - i
|
||||
// i < 0 => |e| = 0
|
||||
// |s| <= i => |e| = 0
|
||||
// |s| <= 0 => |e| = 0
|
||||
// l <= 0 => |e| = 0
|
||||
// |e| = 0 & i >= 0 & |s| > i & |s| > 0 => l <= 0
|
||||
add_axiom(~i_ge_0, ~i_le_ls, ~l_ge_0, mk_seq_eq(xey, s));
|
||||
add_axiom(~i_ge_0, ~i_le_ls, mk_eq(lx, i));
|
||||
add_axiom(~i_ge_0, ~i_le_ls, ~l_ge_0, ~ls_ge_li, mk_eq(le, l));
|
||||
add_axiom(~i_ge_0, ~i_le_ls, ~l_ge_0, ls_ge_li, mk_eq(le, mk_sub(ls, i)));
|
||||
add_axiom(i_ge_0, le_is_0);
|
||||
add_axiom(~ls_le_i, le_is_0);
|
||||
add_axiom(~ls_le_0, le_is_0);
|
||||
add_axiom(~l_le_0, le_is_0);
|
||||
add_axiom(~le_is_0, ~i_ge_0, ls_le_i, ls_le_0, l_le_0);
|
||||
}
|
||||
|
||||
void seq_axioms::add_tail_axiom(expr* e, expr* s) {
|
||||
expr_ref head(m), tail(m);
|
||||
m_sk.decompose(s, head, tail);
|
||||
TRACE("seq", tout << "tail " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << "\n";);
|
||||
literal emp = mk_eq_empty(s);
|
||||
add_axiom(emp, mk_seq_eq(s, mk_concat(head, e)));
|
||||
add_axiom(~emp, mk_eq_empty(e));
|
||||
}
|
||||
|
||||
void seq_axioms::add_drop_last_axiom(expr* e, expr* s) {
|
||||
TRACE("seq", tout << "drop last " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << "\n";);
|
||||
literal emp = mk_eq_empty(s);
|
||||
add_axiom(emp, mk_seq_eq(s, mk_concat(e, seq.str.mk_unit(m_sk.mk_last(s)))));
|
||||
add_axiom(~emp, mk_eq_empty(e));
|
||||
}
|
||||
|
||||
bool seq_axioms::is_drop_last(expr* s, expr* i, expr* l) {
|
||||
rational i1;
|
||||
if (!a.is_numeral(i, i1) || !i1.is_zero()) {
|
||||
return false;
|
||||
}
|
||||
expr_ref l2(m), l1(l, m);
|
||||
l2 = mk_sub(mk_len(s), a.mk_int(1));
|
||||
m_rewrite(l1);
|
||||
m_rewrite(l2);
|
||||
return l1 == l2;
|
||||
}
|
||||
|
||||
bool seq_axioms::is_tail(expr* s, expr* i, expr* l) {
|
||||
rational i1;
|
||||
if (!a.is_numeral(i, i1) || !i1.is_one()) {
|
||||
return false;
|
||||
}
|
||||
expr_ref l2(m), l1(l, m);
|
||||
l2 = mk_sub(mk_len(s), a.mk_int(1));
|
||||
m_rewrite(l1);
|
||||
m_rewrite(l2);
|
||||
return l1 == l2;
|
||||
}
|
||||
|
||||
bool seq_axioms::is_extract_prefix0(expr* s, expr* i, expr* l) {
|
||||
rational i1;
|
||||
return a.is_numeral(i, i1) && i1.is_zero();
|
||||
}
|
||||
|
||||
bool seq_axioms::is_extract_suffix(expr* s, expr* i, expr* l) {
|
||||
expr_ref len(a.mk_add(l, i), m);
|
||||
m_rewrite(len);
|
||||
return seq.str.is_length(len, l) && l == s;
|
||||
}
|
||||
|
||||
/*
|
||||
0 <= l <= len(s) => s = ey & l = len(e)
|
||||
len(s) < l => s = e
|
||||
l < 0 => e = empty
|
||||
*/
|
||||
void seq_axioms::add_extract_prefix_axiom(expr* e, expr* s, expr* l) {
|
||||
TRACE("seq", tout << "prefix " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << " " << mk_bounded_pp(l, m, 2) << "\n";);
|
||||
expr_ref le = mk_len(e);
|
||||
expr_ref ls = mk_len(s);
|
||||
expr_ref ls_minus_l(mk_sub(ls, l), m);
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
expr_ref y = m_sk.mk_post(s, l);
|
||||
expr_ref ey = mk_concat(e, y);
|
||||
literal l_ge_0 = mk_literal(a.mk_ge(l, zero));
|
||||
literal l_le_s = mk_literal(a.mk_le(mk_sub(l, ls), zero));
|
||||
add_axiom(~l_ge_0, ~l_le_s, mk_seq_eq(s, ey));
|
||||
add_axiom(~l_ge_0, ~l_le_s, mk_eq(l, le));
|
||||
add_axiom(~l_ge_0, ~l_le_s, mk_eq(ls_minus_l, mk_len(y)));
|
||||
add_axiom(l_le_s, mk_eq(e, s));
|
||||
add_axiom(l_ge_0, mk_eq_empty(e));
|
||||
}
|
||||
|
||||
/*
|
||||
0 <= i <= len(s) => s = xe & i = len(x)
|
||||
i < 0 => e = empty
|
||||
i > len(s) => e = empty
|
||||
*/
|
||||
void seq_axioms::add_extract_suffix_axiom(expr* e, expr* s, expr* i) {
|
||||
TRACE("seq", tout << "suffix " << mk_bounded_pp(e, m, 2) << " " << mk_bounded_pp(s, m, 2) << "\n";);
|
||||
expr_ref x = m_sk.mk_pre(s, i);
|
||||
expr_ref lx = mk_len(x);
|
||||
expr_ref ls = mk_len(s);
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
expr_ref xe = mk_concat(x, e);
|
||||
literal le_is_0 = mk_eq_empty(e);
|
||||
literal i_ge_0 = mk_literal(a.mk_ge(i, zero));
|
||||
literal i_le_s = mk_literal(a.mk_le(mk_sub(i, ls), zero));
|
||||
add_axiom(~i_ge_0, ~i_le_s, mk_seq_eq(s, xe));
|
||||
add_axiom(~i_ge_0, ~i_le_s, mk_eq(i, lx));
|
||||
add_axiom(i_ge_0, le_is_0);
|
||||
add_axiom(i_le_s, le_is_0);
|
||||
}
|
||||
|
||||
/*
|
||||
encode that s is not contained in of xs1
|
||||
where s1 is all of s, except the last element.
|
||||
|
||||
s = "" or s = s1*(unit c)
|
||||
s = "" or !contains(x*s1, s)
|
||||
*/
|
||||
void seq_axioms::tightest_prefix(expr* s, expr* x) {
|
||||
expr_ref s1 = m_sk.mk_first(s);
|
||||
expr_ref c = m_sk.mk_last(s);
|
||||
expr_ref s1c = mk_concat(s1, seq.str.mk_unit(c));
|
||||
literal s_eq_emp = mk_eq_empty(s);
|
||||
add_axiom(s_eq_emp, mk_seq_eq(s, s1c));
|
||||
add_axiom(s_eq_emp, ~mk_literal(seq.str.mk_contains(mk_concat(x, s1), s)));
|
||||
}
|
||||
|
||||
/*
|
||||
[[str.indexof]](w, w2, i) is the smallest n such that for some some w1, w3
|
||||
- w = w1w2w3
|
||||
- i <= n = |w1|
|
||||
|
||||
if [[str.contains]](w, w2) = true, |w2| > 0 and i >= 0.
|
||||
|
||||
[[str.indexof]](w,w2,i) = -1 otherwise.
|
||||
|
||||
|
||||
let i = Index(t, s, offset):
|
||||
// index of s in t starting at offset.
|
||||
|
||||
|
||||
|t| = 0 => |s| = 0 or indexof(t,s,offset) = -1
|
||||
|t| = 0 & |s| = 0 => indexof(t,s,offset) = 0
|
||||
|
||||
offset >= len(t) => |s| = 0 or i = -1
|
||||
|
||||
len(t) != 0 & !contains(t, s) => i = -1
|
||||
|
||||
|
||||
offset = 0 & len(t) != 0 & contains(t, s) => t = xsy & i = len(x)
|
||||
tightest_prefix(x, s)
|
||||
|
||||
|
||||
0 <= offset < len(t) => xy = t &
|
||||
len(x) = offset &
|
||||
(-1 = indexof(y, s, 0) => -1 = i) &
|
||||
(indexof(y, s, 0) >= 0 => indexof(t, s, 0) + offset = i)
|
||||
|
||||
offset < 0 => i = -1
|
||||
|
||||
optional lemmas:
|
||||
(len(s) > len(t) -> i = -1)
|
||||
(len(s) <= len(t) -> i <= len(t)-len(s))
|
||||
*/
|
||||
void seq_axioms::add_indexof_axiom(expr* i) {
|
||||
expr* s = nullptr, *t = nullptr, *offset = nullptr;
|
||||
rational r;
|
||||
VERIFY(seq.str.is_index(i, t, s) ||
|
||||
seq.str.is_index(i, t, s, offset));
|
||||
expr_ref minus_one(a.mk_int(-1), m);
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
expr_ref xsy(m);
|
||||
|
||||
literal cnt = mk_literal(seq.str.mk_contains(t, s));
|
||||
literal i_eq_m1 = mk_eq(i, minus_one);
|
||||
literal i_eq_0 = mk_eq(i, zero);
|
||||
literal s_eq_empty = mk_eq_empty(s);
|
||||
literal t_eq_empty = mk_eq_empty(t);
|
||||
|
||||
// |t| = 0 => |s| = 0 or indexof(t,s,offset) = -1
|
||||
// ~contains(t,s) <=> indexof(t,s,offset) = -1
|
||||
|
||||
add_axiom(cnt, i_eq_m1);
|
||||
add_axiom(~t_eq_empty, s_eq_empty, i_eq_m1);
|
||||
|
||||
if (!offset || (a.is_numeral(offset, r) && r.is_zero())) {
|
||||
expr_ref x = m_sk.mk_indexof_left(t, s);
|
||||
expr_ref y = m_sk.mk_indexof_right(t, s);
|
||||
xsy = mk_concat(x, s, y);
|
||||
expr_ref lenx = mk_len(x);
|
||||
// |s| = 0 => indexof(t,s,0) = 0
|
||||
// contains(t,s) & |s| != 0 => t = xsy & indexof(t,s,0) = |x|
|
||||
add_axiom(~s_eq_empty, i_eq_0);
|
||||
add_axiom(~cnt, s_eq_empty, mk_seq_eq(t, xsy));
|
||||
add_axiom(~cnt, s_eq_empty, mk_eq(i, lenx));
|
||||
add_axiom(~cnt, mk_literal(a.mk_ge(i, zero)));
|
||||
tightest_prefix(s, x);
|
||||
}
|
||||
else {
|
||||
// offset >= len(t) => |s| = 0 or indexof(t, s, offset) = -1
|
||||
// offset > len(t) => indexof(t, s, offset) = -1
|
||||
// offset = len(t) & |s| = 0 => indexof(t, s, offset) = offset
|
||||
expr_ref len_t = mk_len(t);
|
||||
literal offset_ge_len = mk_literal(a.mk_ge(mk_sub(offset, len_t), zero));
|
||||
literal offset_le_len = mk_literal(a.mk_le(mk_sub(offset, len_t), zero));
|
||||
literal i_eq_offset = mk_eq(i, offset);
|
||||
add_axiom(~offset_ge_len, s_eq_empty, i_eq_m1);
|
||||
add_axiom(offset_le_len, i_eq_m1);
|
||||
add_axiom(~offset_ge_len, ~offset_le_len, ~s_eq_empty, i_eq_offset);
|
||||
|
||||
expr_ref x = m_sk.mk_indexof_left(t, s, offset);
|
||||
expr_ref y = m_sk.mk_indexof_right(t, s, offset);
|
||||
expr_ref indexof0(seq.str.mk_index(y, s, zero), m);
|
||||
expr_ref offset_p_indexof0(a.mk_add(offset, indexof0), m);
|
||||
literal offset_ge_0 = mk_literal(a.mk_ge(offset, zero));
|
||||
|
||||
// 0 <= offset & offset < len(t) => t = xy
|
||||
// 0 <= offset & offset < len(t) => len(x) = offset
|
||||
// 0 <= offset & offset < len(t) & indexof(y,s,0) = -1 => -1 = i
|
||||
// 0 <= offset & offset < len(t) & indexof(y,s,0) >= 0 =>
|
||||
// -1 = indexof(y,s,0) + offset = indexof(t, s, offset)
|
||||
|
||||
add_axiom(~offset_ge_0, offset_ge_len, mk_seq_eq(t, mk_concat(x, y)));
|
||||
add_axiom(~offset_ge_0, offset_ge_len, mk_eq(mk_len(x), offset));
|
||||
add_axiom(~offset_ge_0, offset_ge_len,
|
||||
~mk_eq(indexof0, minus_one), i_eq_m1);
|
||||
add_axiom(~offset_ge_0, offset_ge_len,
|
||||
~mk_literal(a.mk_ge(indexof0, zero)),
|
||||
mk_eq(offset_p_indexof0, i));
|
||||
|
||||
// offset < 0 => -1 = i
|
||||
add_axiom(offset_ge_0, i_eq_m1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
!contains(t, s) => i = -1
|
||||
|t| = 0 => |s| = 0 or i = -1
|
||||
|t| = 0 & |s| = 0 => i = 0
|
||||
|t| != 0 & contains(t, s) => t = xsy & i = len(x)
|
||||
|s| = 0 or s = s_head*s_tail
|
||||
|s| = 0 or !contains(s_tail*y, s)
|
||||
|
||||
*/
|
||||
void seq_axioms::add_last_indexof_axiom(expr* i) {
|
||||
expr* s = nullptr, *t = nullptr;
|
||||
VERIFY(seq.str.is_last_index(i, t, s));
|
||||
expr_ref minus_one(a.mk_int(-1), m);
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
expr_ref s_head(m), s_tail(m);
|
||||
expr_ref x = m_sk.mk_last_indexof_left(t, s);
|
||||
expr_ref y = m_sk.mk_last_indexof_right(t, s);
|
||||
m_sk.decompose(s, s_head, s_tail);
|
||||
literal cnt = mk_literal(seq.str.mk_contains(t, s));
|
||||
literal cnt2 = mk_literal(seq.str.mk_contains(mk_concat(s_tail, y), s));
|
||||
literal i_eq_m1 = mk_eq(i, minus_one);
|
||||
literal i_eq_0 = mk_eq(i, zero);
|
||||
literal s_eq_empty = mk_eq_empty(s);
|
||||
literal t_eq_empty = mk_eq_empty(t);
|
||||
expr_ref xsy = mk_concat(x, s, y);
|
||||
|
||||
add_axiom(cnt, i_eq_m1);
|
||||
add_axiom(~t_eq_empty, s_eq_empty, i_eq_m1);
|
||||
add_axiom(~t_eq_empty, ~s_eq_empty, i_eq_0);
|
||||
add_axiom(t_eq_empty, ~cnt, mk_seq_eq(t, xsy));
|
||||
add_axiom(t_eq_empty, ~cnt, mk_eq(i, mk_len(x)));
|
||||
add_axiom(s_eq_empty, mk_eq(s, mk_concat(s_head, s_tail)));
|
||||
add_axiom(s_eq_empty, ~cnt2);
|
||||
}
|
||||
|
||||
/*
|
||||
let r = replace(a, s, t)
|
||||
|
||||
a = "" => s = "" or r = a
|
||||
contains(a, s) or r = a
|
||||
s = "" => r = t+a
|
||||
|
||||
tightest_prefix(s, x)
|
||||
(contains(a, s) -> r = xty & a = xsy) &
|
||||
(!contains(a, s) -> r = a)
|
||||
|
||||
*/
|
||||
void seq_axioms::add_replace_axiom(expr* r) {
|
||||
expr* u = nullptr, *s = nullptr, *t = nullptr;
|
||||
VERIFY(seq.str.is_replace(r, u, s, t));
|
||||
expr_ref x = m_sk.mk_indexof_left(u, s);
|
||||
expr_ref y = m_sk.mk_indexof_right(u, s);
|
||||
expr_ref xty = mk_concat(x, t, y);
|
||||
expr_ref xsy = mk_concat(x, s, y);
|
||||
literal a_emp = mk_eq_empty(u, true);
|
||||
literal s_emp = mk_eq_empty(u, true);
|
||||
literal cnt = mk_literal(seq.str.mk_contains(u, s));
|
||||
add_axiom(~a_emp, s_emp, mk_seq_eq(r, u));
|
||||
add_axiom(cnt, mk_seq_eq(r, u));
|
||||
add_axiom(~s_emp, mk_seq_eq(r, mk_concat(t, u)));
|
||||
add_axiom(~cnt, a_emp, s_emp, mk_seq_eq(u, xsy));
|
||||
add_axiom(~cnt, a_emp, s_emp, mk_seq_eq(r, xty));
|
||||
ctx().force_phase(cnt);
|
||||
tightest_prefix(s, x);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
let e = at(s, i)
|
||||
|
||||
0 <= i < len(s) -> s = xey & len(x) = i & len(e) = 1
|
||||
i < 0 \/ i >= len(s) -> e = empty
|
||||
|
||||
*/
|
||||
void seq_axioms::add_at_axiom(expr* e) {
|
||||
TRACE("seq", tout << "at-axiom: " << ctx().get_scope_level() << " " << mk_bounded_pp(e, m) << "\n";);
|
||||
expr* s = nullptr, *i = nullptr;
|
||||
VERIFY(seq.str.is_at(e, s, i));
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
expr_ref one(a.mk_int(1), m);
|
||||
expr_ref emp(seq.str.mk_empty(m.get_sort(e)), m);
|
||||
expr_ref len_s = mk_len(s);
|
||||
literal i_ge_0 = mk_literal(a.mk_ge(i, zero));
|
||||
literal i_ge_len_s = mk_literal(a.mk_ge(mk_sub(i, mk_len(s)), zero));
|
||||
expr_ref len_e = mk_len(e);
|
||||
|
||||
rational iv;
|
||||
if (a.is_numeral(i, iv) && iv.is_unsigned()) {
|
||||
expr_ref_vector es(m);
|
||||
expr_ref nth(m);
|
||||
unsigned k = iv.get_unsigned();
|
||||
for (unsigned j = 0; j <= k; ++j) {
|
||||
es.push_back(seq.str.mk_unit(mk_nth(s, a.mk_int(j))));
|
||||
}
|
||||
nth = es.back();
|
||||
es.push_back(m_sk.mk_tail(s, i));
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(s, seq.str.mk_concat(es)));
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_seq_eq(nth, e));
|
||||
}
|
||||
else {
|
||||
expr_ref x = m_sk.mk_pre(s, i);
|
||||
expr_ref y = m_sk.mk_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(i, len_x));
|
||||
}
|
||||
|
||||
add_axiom(i_ge_0, mk_eq(e, emp));
|
||||
add_axiom(~i_ge_len_s, mk_eq(e, emp));
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(one, len_e));
|
||||
add_axiom(mk_literal(a.mk_le(len_e, one)));
|
||||
}
|
||||
|
||||
/**
|
||||
i >= 0 i < len(s) => unit(nth_i(s, i)) = at(s, i)
|
||||
nth_i(unit(nth_i(s, i)), 0) = nth_i(s, i)
|
||||
*/
|
||||
|
||||
void seq_axioms::add_nth_axiom(expr* e) {
|
||||
expr* s = nullptr, *i = nullptr;
|
||||
rational n;
|
||||
zstring str;
|
||||
VERIFY(seq.str.is_nth_i(e, s, i));
|
||||
if (seq.str.is_string(s, str) && a.is_numeral(i, n) &&
|
||||
n.is_unsigned() && n.get_unsigned() < str.length()) {
|
||||
app_ref ch(seq.str.mk_char(str[n.get_unsigned()]), m);
|
||||
add_axiom(mk_eq(ch, e));
|
||||
}
|
||||
else {
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
literal i_ge_0 = mk_literal(a.mk_ge(i, zero));
|
||||
literal i_ge_len_s = mk_literal(a.mk_ge(mk_sub(i, mk_len(s)), zero));
|
||||
// at(s,i) = [nth(s,i)]
|
||||
expr_ref rhs(s, m);
|
||||
expr_ref lhs(seq.str.mk_unit(e), m);
|
||||
if (!seq.str.is_at(s) || zero != i) rhs = seq.str.mk_at(s, i);
|
||||
m_rewrite(rhs);
|
||||
add_axiom(~i_ge_0, i_ge_len_s, mk_eq(lhs, rhs));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void seq_axioms::add_itos_axiom(expr* e) {
|
||||
expr* n = nullptr;
|
||||
TRACE("seq", tout << mk_pp(e, m) << "\n";);
|
||||
VERIFY(seq.str.is_itos(e, n));
|
||||
|
||||
// itos(n) = "" <=> n < 0
|
||||
expr_ref zero(a.mk_int(0), m);
|
||||
literal eq1 = mk_literal(seq.str.mk_is_empty(e));
|
||||
literal ge0 = mk_literal(a.mk_ge(n, zero));
|
||||
// n >= 0 => itos(n) != ""
|
||||
// itos(n) = "" or n >= 0
|
||||
add_axiom(~eq1, ~ge0);
|
||||
add_axiom(eq1, ge0);
|
||||
add_axiom(mk_literal(a.mk_ge(mk_len(e), a.mk_int(0))));
|
||||
|
||||
// n >= 0 => stoi(itos(n)) = n
|
||||
app_ref stoi(seq.str.mk_stoi(e), m);
|
||||
add_axiom(~ge0, th.mk_preferred_eq(stoi, n));
|
||||
|
||||
// itos(n) does not start with "0" when n > 0
|
||||
// n = 0 or at(itos(n),0) != "0"
|
||||
// alternative: n >= 0 => itos(stoi(itos(n))) = itos(n)
|
||||
add_axiom(mk_eq(n, zero),
|
||||
~mk_eq(seq.str.mk_at(e,zero),
|
||||
seq.str.mk_string(symbol("0"))));
|
||||
}
|
||||
|
||||
/**
|
||||
stoi(s) >= -1
|
||||
stoi("") = -1
|
||||
*/
|
||||
void seq_axioms::add_stoi_axiom(expr* e) {
|
||||
TRACE("seq", tout << mk_pp(e, m) << "\n";);
|
||||
expr* s = nullptr;
|
||||
VERIFY (seq.str.is_stoi(e, s));
|
||||
add_axiom(mk_literal(a.mk_ge(e, a.mk_int(-1))));
|
||||
add_axiom(~mk_literal(seq.str.mk_is_empty(s)), mk_eq(seq.str.mk_stoi(s), a.mk_int(-1)));
|
||||
}
|
||||
|
||||
/**
|
||||
e1 < e2 => prefix(e1, e2) or e1 = xcy e1 < e2 => prefix(e1, e2) or
|
||||
c < d e1 < e2 => prefix(e1, e2) or e2 = xdz e1 < e2 => e1 != e2
|
||||
!(e1 < e2) => prefix(e2, e1) or e2 = xdz !(e1 < e2) => prefix(e2,
|
||||
e1) or d < c !(e1 < e2) => prefix(e2, e1) or e1 = xcy !(e1 = e2) or
|
||||
!(e1 < e2) optional: e1 < e2 or e1 = e2 or e2 < e1 !(e1 = e2) or
|
||||
!(e2 < e1) !(e1 < e2) or !(e2 < e1)
|
||||
*/
|
||||
void seq_axioms::add_lt_axiom(expr* n) {
|
||||
expr* e1 = nullptr, *e2 = nullptr;
|
||||
VERIFY(seq.str.is_lt(n, e1, e2));
|
||||
sort* s = m.get_sort(e1);
|
||||
sort* char_sort = nullptr;
|
||||
VERIFY(seq.is_seq(s, char_sort));
|
||||
literal lt = mk_literal(n);
|
||||
expr_ref x = m_sk.mk(symbol("str.lt.x"), e1, e2);
|
||||
expr_ref y = m_sk.mk(symbol("str.lt.y"), e1, e2);
|
||||
expr_ref z = m_sk.mk(symbol("str.lt.z"), e1, e2);
|
||||
expr_ref c = m_sk.mk(symbol("str.lt.c"), e1, e2, char_sort);
|
||||
expr_ref d = m_sk.mk(symbol("str.lt.d"), e1, e2, char_sort);
|
||||
expr_ref xcy = mk_concat(x, seq.str.mk_unit(c), y);
|
||||
expr_ref xdz = mk_concat(x, seq.str.mk_unit(d), z);
|
||||
literal eq = mk_eq(e1, e2);
|
||||
literal pref21 = mk_literal(seq.str.mk_prefix(e2, e1));
|
||||
literal pref12 = mk_literal(seq.str.mk_prefix(e1, e2));
|
||||
literal e1xcy = mk_eq(e1, xcy);
|
||||
literal e2xdz = mk_eq(e2, xdz);
|
||||
literal ltcd = mk_literal(seq.mk_lt(c, d));
|
||||
literal ltdc = mk_literal(seq.mk_lt(d, c));
|
||||
add_axiom(~lt, pref12, e2xdz);
|
||||
add_axiom(~lt, pref12, e1xcy);
|
||||
add_axiom(~lt, pref12, ltcd);
|
||||
add_axiom(lt, pref21, e1xcy);
|
||||
add_axiom(lt, pref21, ltdc);
|
||||
add_axiom(lt, pref21, e2xdz);
|
||||
add_axiom(~eq, ~lt);
|
||||
}
|
||||
|
||||
/**
|
||||
e1 <= e2 <=> e1 < e2 or e1 = e2
|
||||
*/
|
||||
void seq_axioms::add_le_axiom(expr* n) {
|
||||
expr* e1 = nullptr, *e2 = nullptr;
|
||||
VERIFY(seq.str.is_le(n, e1, e2));
|
||||
literal lt = mk_literal(seq.str.mk_lex_lt(e1, e2));
|
||||
literal le = mk_literal(n);
|
||||
literal eq = mk_eq(e1, e2);
|
||||
add_axiom(~le, lt, eq);
|
||||
add_axiom(~eq, le);
|
||||
add_axiom(~lt, le);
|
||||
}
|
||||
|
||||
void seq_axioms::add_unit_axiom(expr* n) {
|
||||
expr* u = nullptr;
|
||||
VERIFY(seq.str.is_unit(n, u));
|
||||
add_axiom(mk_eq(u, m_sk.mk_unit_inv(n)));
|
||||
}
|
||||
|
84
src/smt/seq_axioms.h
Normal file
84
src/smt/seq_axioms.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
seq_axioms.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Axiomatize string operations that can be reduced to
|
||||
more basic operations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2020-4-16
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "ast/seq_decl_plugin.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "smt/smt_theory.h"
|
||||
#include "smt/seq_skolem.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
class seq_axioms {
|
||||
theory& th;
|
||||
th_rewriter& m_rewrite;
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
seq_util seq;
|
||||
seq_skolem m_sk;
|
||||
|
||||
literal mk_eq_empty(expr* e, bool phase = true) { return mk_eq_empty2(e, phase); }
|
||||
context& ctx() { return th.get_context(); }
|
||||
literal mk_eq(expr* a, expr* b);
|
||||
literal mk_literal(expr* e);
|
||||
literal mk_seq_eq(expr* a, expr* b) { SASSERT(seq.is_seq(a) && seq.is_seq(b)); return mk_literal(m_sk.mk_eq(a, b)); }
|
||||
|
||||
expr_ref mk_len(expr* s) { return expr_ref(seq.str.mk_length(s), m); }
|
||||
expr_ref mk_sub(expr* x, expr* y) { return expr_ref(a.mk_sub(x, y), m); }
|
||||
expr_ref mk_concat(expr* e1, expr* e2, expr* e3) { return expr_ref(seq.str.mk_concat(e1, e2, e3), m); }
|
||||
expr_ref mk_concat(expr* e1, expr* e2) { return expr_ref(seq.str.mk_concat(e1, e2), m); }
|
||||
expr_ref mk_nth(expr* e, expr* i) { return expr_ref(seq.str.mk_nth_i(e, i), m); }
|
||||
void add_axiom(literal l1, literal l2 = null_literal, literal l3 = null_literal,
|
||||
literal l4 = null_literal, literal l5 = null_literal) { add_axiom5(l1, l2, l3, l4, l5); }
|
||||
|
||||
void add_tail_axiom(expr* e, expr* s);
|
||||
void add_drop_last_axiom(expr* e, expr* s);
|
||||
bool is_drop_last(expr* s, expr* i, expr* l);
|
||||
bool is_tail(expr* s, expr* i, expr* l);
|
||||
bool is_extract_prefix0(expr* s, expr* i, expr* l);
|
||||
bool is_extract_suffix(expr* s, expr* i, expr* l);
|
||||
void add_extract_prefix_axiom(expr* e, expr* s, expr* l);
|
||||
void add_extract_suffix_axiom(expr* e, expr* s, expr* i);
|
||||
void tightest_prefix(expr* s, expr* x);
|
||||
|
||||
public:
|
||||
|
||||
seq_axioms(theory& th, th_rewriter& r);
|
||||
|
||||
// we rely on client to supply the following functions:
|
||||
std::function<void(literal l1, literal l2, literal l3, literal l4, literal l5)> add_axiom5;
|
||||
std::function<literal(expr*,bool)> mk_eq_empty2;
|
||||
|
||||
void add_extract_axiom(expr* n);
|
||||
void add_indexof_axiom(expr* n);
|
||||
void add_last_indexof_axiom(expr* n);
|
||||
void add_replace_axiom(expr* n);
|
||||
void add_at_axiom(expr* n);
|
||||
void add_nth_axiom(expr* n);
|
||||
void add_itos_axiom(expr* n);
|
||||
void add_stoi_axiom(expr* n);
|
||||
void add_lt_axiom(expr* n);
|
||||
void add_le_axiom(expr* n);
|
||||
void add_unit_axiom(expr* n);
|
||||
};
|
||||
|
||||
};
|
||||
|
171
src/smt/seq_skolem.cpp
Normal file
171
src/smt/seq_skolem.cpp
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
seq_skolem.cpp
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2020-4-16
|
||||
|
||||
--*/
|
||||
|
||||
#include "smt/seq_skolem.h"
|
||||
|
||||
using namespace smt;
|
||||
|
||||
|
||||
seq_skolem::seq_skolem(ast_manager& m, th_rewriter& rw):
|
||||
m(m),
|
||||
m_rewrite(rw),
|
||||
seq(m),
|
||||
a(m) {
|
||||
m_prefix = "seq.p.suffix";
|
||||
m_suffix = "seq.s.prefix";
|
||||
m_accept = "aut.accept";
|
||||
m_tail = "seq.tail";
|
||||
m_seq_first = "seq.first";
|
||||
m_seq_last = "seq.last";
|
||||
m_indexof_left = "seq.idx.left";
|
||||
m_indexof_right = "seq.idx.right";
|
||||
m_aut_step = "aut.step";
|
||||
m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l
|
||||
m_post = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l
|
||||
m_eq = "seq.eq";
|
||||
m_seq_align = "seq.align";
|
||||
m_max_unfolding = "seq.max_unfolding";
|
||||
}
|
||||
|
||||
expr_ref seq_skolem::mk(symbol const& s, expr* e1, expr* e2, expr* e3, expr* e4, sort* range) {
|
||||
expr* es[4] = { e1, e2, e3, e4 };
|
||||
unsigned len = e4?4:(e3?3:(e2?2:(e1?1:0)));
|
||||
if (!range) {
|
||||
range = m.get_sort(e1);
|
||||
}
|
||||
return expr_ref(seq.mk_skolem(s, len, es, range), m);
|
||||
}
|
||||
|
||||
expr_ref seq_skolem::mk_max_unfolding_depth(unsigned depth) {
|
||||
parameter ps[2] = { parameter(m_max_unfolding), parameter(depth) };
|
||||
func_decl* f = m.mk_func_decl(seq.get_family_id(), _OP_SEQ_SKOLEM, 2, ps, 0, (sort*const*) nullptr, m.mk_bool_sort());
|
||||
return expr_ref(m.mk_const(f), m);
|
||||
}
|
||||
|
||||
bool seq_skolem::is_skolem(symbol const& s, expr* e) const {
|
||||
return seq.is_skolem(e) && to_app(e)->get_decl()->get_parameter(0).get_symbol() == s;
|
||||
}
|
||||
|
||||
void seq_skolem::decompose(expr* e, expr_ref& head, expr_ref& tail) {
|
||||
expr* e1 = nullptr, *e2 = nullptr;
|
||||
zstring s;
|
||||
rational r;
|
||||
if (seq.str.is_empty(e)) {
|
||||
head = seq.str.mk_unit(seq.str.mk_nth(e, a.mk_int(0)));
|
||||
tail = e;
|
||||
}
|
||||
else if (seq.str.is_string(e, s)) {
|
||||
head = seq.str.mk_unit(seq.str.mk_char(s, 0));
|
||||
tail = seq.str.mk_string(s.extract(1, s.length()-1));
|
||||
}
|
||||
else if (seq.str.is_unit(e)) {
|
||||
head = e;
|
||||
tail = seq.str.mk_empty(m.get_sort(e));
|
||||
}
|
||||
else if (seq.str.is_concat(e, e1, e2) && seq.str.is_empty(e1)) {
|
||||
decompose(e2, head, tail);
|
||||
}
|
||||
else if (seq.str.is_concat(e, e1, e2) && seq.str.is_string(e1, s) && s.length() > 0) {
|
||||
head = seq.str.mk_unit(seq.str.mk_char(s, 0));
|
||||
tail = seq.str.mk_concat(seq.str.mk_string(s.extract(1, s.length()-1)), e2);
|
||||
}
|
||||
else if (seq.str.is_concat(e, e1, e2) && seq.str.is_unit(e1)) {
|
||||
head = e1;
|
||||
tail = e2;
|
||||
}
|
||||
else if (is_skolem(m_tail, e) && a.is_numeral(to_app(e)->get_arg(1), r)) {
|
||||
expr* s = to_app(e)->get_arg(0);
|
||||
expr* idx = a.mk_int(r.get_unsigned() + 1);
|
||||
head = seq.str.mk_unit(seq.str.mk_nth(s, idx));
|
||||
tail = mk(m_tail, s, idx);
|
||||
m_rewrite(head);
|
||||
}
|
||||
else {
|
||||
head = seq.str.mk_unit(seq.str.mk_nth(e, a.mk_int(0)));
|
||||
m_rewrite(head);
|
||||
tail = mk(m_tail, e, a.mk_int(0));
|
||||
}
|
||||
}
|
||||
|
||||
bool seq_skolem::is_step(expr* e, expr*& s, expr*& idx, expr*& re, expr*& i, expr*& j, expr*& t) const {
|
||||
if (is_step(e)) {
|
||||
s = to_app(e)->get_arg(0);
|
||||
idx = to_app(e)->get_arg(1);
|
||||
re = to_app(e)->get_arg(2);
|
||||
i = to_app(e)->get_arg(3);
|
||||
j = to_app(e)->get_arg(4);
|
||||
t = to_app(e)->get_arg(5);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool seq_skolem::is_tail(expr* e, expr*& s, unsigned& idx) const {
|
||||
expr* i = nullptr;
|
||||
rational r;
|
||||
return is_tail_match(e, s, i) && a.is_numeral(i, r) && r.is_unsigned() && (idx = r.get_unsigned(), true);
|
||||
}
|
||||
|
||||
bool seq_skolem::is_tail_match(expr* e, expr*& s, expr*& idx) const {
|
||||
return is_tail(e) && (s = to_app(e)->get_arg(0), idx = to_app(e)->get_arg(1), true);
|
||||
}
|
||||
|
||||
bool seq_skolem::is_eq(expr* e, expr*& a, expr*& b) const {
|
||||
return is_skolem(m_eq, e) && (a = to_app(e)->get_arg(0), b = to_app(e)->get_arg(1), true);
|
||||
}
|
||||
|
||||
bool seq_skolem::is_pre(expr* e, expr*& s, expr*& i) {
|
||||
return is_skolem(m_pre, e) && (s = to_app(e)->get_arg(0), i = to_app(e)->get_arg(1), true);
|
||||
}
|
||||
|
||||
bool seq_skolem::is_post(expr* e, expr*& s, expr*& start) {
|
||||
return is_skolem(m_post, e) && (s = to_app(e)->get_arg(0), start = to_app(e)->get_arg(1), true);
|
||||
}
|
||||
|
||||
expr_ref seq_skolem::mk_unit_inv(expr* n) {
|
||||
expr* u = nullptr;
|
||||
VERIFY(seq.str.is_unit(n, u));
|
||||
sort* s = m.get_sort(u);
|
||||
return mk(symbol("seq.unit-inv"), n, nullptr, nullptr, nullptr, s);
|
||||
}
|
||||
|
||||
|
||||
expr_ref seq_skolem::mk_last(expr* s) {
|
||||
zstring str;
|
||||
if (seq.str.is_string(s, str) && str.length() > 0) {
|
||||
return expr_ref(seq.str.mk_char(str, str.length()-1), m);
|
||||
}
|
||||
sort* char_sort = nullptr;
|
||||
VERIFY(seq.is_seq(m.get_sort(s), char_sort));
|
||||
return mk(m_seq_last, s, char_sort);
|
||||
}
|
||||
|
||||
expr_ref seq_skolem::mk_first(expr* s) {
|
||||
zstring str;
|
||||
if (seq.str.is_string(s, str) && str.length() > 0) {
|
||||
return expr_ref(seq.str.mk_string(str.extract(0, str.length()-1)), m);
|
||||
}
|
||||
return mk(m_seq_first, s);
|
||||
}
|
||||
|
||||
expr_ref seq_skolem::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned j, expr* t) {
|
||||
expr_ref_vector args(m);
|
||||
args.push_back(s).push_back(idx).push_back(re);
|
||||
args.push_back(a.mk_int(i));
|
||||
args.push_back(a.mk_int(j));
|
||||
args.push_back(t);
|
||||
return expr_ref(seq.mk_skolem(m_aut_step, args.size(), args.c_ptr(), m.mk_bool_sort()), m);
|
||||
}
|
||||
|
107
src/smt/seq_skolem.h
Normal file
107
src/smt/seq_skolem.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
seq_skolem.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Skolem function support for sequences.
|
||||
Skolem functions are auxiliary funcions useful for axiomatizing sequence
|
||||
operations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2020-4-16
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "ast/seq_decl_plugin.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
class seq_skolem {
|
||||
ast_manager& m;
|
||||
th_rewriter& m_rewrite; // NB: would be nicer not to have this dependency
|
||||
seq_util seq;
|
||||
arith_util a;
|
||||
|
||||
symbol m_prefix, m_suffix;
|
||||
symbol m_tail;
|
||||
symbol m_seq_first, m_seq_last;
|
||||
symbol m_indexof_left, m_indexof_right; // inverse of indexof: (indexof_left s t) + s + (indexof_right s t) = t, for s in t.
|
||||
symbol m_aut_step; // regex unfolding state
|
||||
symbol m_accept, m_reject; // regex
|
||||
symbol m_pre, m_post; // inverse of at: (pre s i) + (at s i) + (post s i) = s if 0 <= i < (len s)
|
||||
symbol m_eq; // equality atom
|
||||
symbol m_seq_align;
|
||||
symbol m_max_unfolding;
|
||||
|
||||
public:
|
||||
|
||||
seq_skolem(ast_manager& m, th_rewriter& r);
|
||||
|
||||
expr_ref mk(symbol const& s, sort* range) { return mk(s, nullptr, nullptr, nullptr, nullptr, range); }
|
||||
expr_ref mk(symbol const& s, expr* e, sort* range) { return mk(s, e, nullptr, nullptr, nullptr, range); }
|
||||
expr_ref mk(symbol const& s, expr* e1, expr* e2, sort* range) { return mk(s, e1, e2, nullptr, nullptr, range); }
|
||||
expr_ref mk(symbol const& s, expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, expr* e4 = nullptr, sort* range = nullptr);
|
||||
|
||||
expr_ref mk(char const* s, sort* range) { return mk(s, nullptr, nullptr, nullptr, nullptr, range); }
|
||||
expr_ref mk(char const* s, expr* e, sort* range) { return mk(s, e, nullptr, nullptr, nullptr, range); }
|
||||
expr_ref mk(char const* s, expr* e1, expr* e2, sort* range) { return mk(s, e1, e2, nullptr, nullptr, range); }
|
||||
expr_ref mk(char const* s , expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, expr* e4 = nullptr, sort* range = nullptr) {
|
||||
return mk(symbol(s), e1, e2, e3, e4, range);
|
||||
}
|
||||
|
||||
expr_ref mk_align(expr* e1, expr* e2, expr* e3, expr* e4) { return mk(m_seq_align, e1, e2, e3, e4); }
|
||||
expr_ref mk_accept(expr_ref_vector const& args) { return expr_ref(seq.mk_skolem(m_accept, args.size(), args.c_ptr(), m.mk_bool_sort()), m); }
|
||||
expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); }
|
||||
expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); }
|
||||
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_left", t, s, offset); }
|
||||
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_right", t, s, offset); }
|
||||
|
||||
expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); }
|
||||
expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); }
|
||||
expr_ref mk_ite(expr* c, expr* t, expr* e) { return mk(symbol("seq.if"), c, t, e, nullptr, m.get_sort(t)); }
|
||||
expr_ref mk_last(expr* s);
|
||||
expr_ref mk_first(expr* s);
|
||||
expr_ref mk_pre(expr* s, expr* i) { return mk(m_pre, s, i); }
|
||||
expr_ref mk_eq(expr* a, expr* b) { return mk(m_eq, a, b, nullptr, nullptr, m.mk_bool_sort()); }
|
||||
expr_ref mk_prefix_inv(expr* s, expr* t) { return mk(m_prefix, s, t); }
|
||||
expr_ref mk_suffix_inv(expr* s, expr* t) { return mk(m_suffix, s, t); }
|
||||
expr_ref mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned j, expr* t);
|
||||
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_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); }
|
||||
bool is_max_unfolding(expr* e) const { return is_skolem(m_max_unfolding, e); }
|
||||
expr_ref mk_max_unfolding_depth(unsigned d);
|
||||
|
||||
bool is_skolem(symbol const& s, expr* e) const;
|
||||
bool is_skolem(expr* e) const { return seq.is_skolem(e); }
|
||||
|
||||
bool is_tail(expr* e) const { return is_skolem(m_tail, e); }
|
||||
bool is_seq_first(expr* e) const { return is_skolem(m_seq_first, e); }
|
||||
bool is_indexof_left(expr* e) const { return is_skolem(m_indexof_left, e); }
|
||||
bool is_indexof_right(expr* e) const { return is_skolem(m_indexof_right, e); }
|
||||
bool is_step(expr* e) const { return is_skolem(m_aut_step, e); }
|
||||
bool is_step(expr* e, expr*& s, expr*& idx, expr*& re, expr*& i, expr*& j, expr*& t) const;
|
||||
bool is_accept(expr* acc) const { return is_skolem(m_accept, acc); }
|
||||
bool is_post(expr* e, expr*& s, expr*& start);
|
||||
bool is_pre(expr* e, expr*& s, expr*& i);
|
||||
bool is_eq(expr* e, expr*& a, expr*& b) const;
|
||||
bool is_tail_match(expr* e, expr*& s, expr*& idx) const;
|
||||
bool is_tail(expr* e, expr*& s, unsigned& idx) const;
|
||||
bool is_digit(expr* e) const { return is_skolem(symbol("seq.is_digit"), e); }
|
||||
|
||||
void decompose(expr* e, expr_ref& head, expr_ref& tail);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
@ -129,6 +129,24 @@ namespace smt {
|
|||
return ctx.get_literal(eq);
|
||||
}
|
||||
|
||||
literal theory::mk_preferred_eq(expr* a, expr* b) {
|
||||
context& ctx = get_context();
|
||||
ctx.assume_eq(ensure_enode(a), ensure_enode(b));
|
||||
literal lit = mk_eq(a, b, false);
|
||||
ctx.force_phase(lit);
|
||||
return lit;
|
||||
}
|
||||
|
||||
enode* theory::ensure_enode(expr* e) {
|
||||
context& ctx = get_context();
|
||||
if (!ctx.e_internalized(e)) {
|
||||
ctx.internalize(e, false);
|
||||
}
|
||||
enode* n = ctx.get_enode(e);
|
||||
ctx.mark_as_relevant(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
theory::theory(family_id fid):
|
||||
m_id(fid),
|
||||
m_context(nullptr),
|
||||
|
|
|
@ -503,6 +503,10 @@ namespace smt {
|
|||
|
||||
literal mk_eq(expr * a, expr * b, bool gate_ctx);
|
||||
|
||||
literal mk_preferred_eq(expr* a, expr* b);
|
||||
|
||||
enode* ensure_enode(expr* e);
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Model generation
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -31,6 +31,8 @@ Revision History:
|
|||
#include "smt/smt_theory.h"
|
||||
#include "smt/smt_arith_value.h"
|
||||
#include "smt/theory_seq_empty.h"
|
||||
#include "smt/seq_skolem.h"
|
||||
#include "smt/seq_axioms.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
|
@ -398,12 +400,11 @@ namespace smt {
|
|||
seq_rewriter m_seq_rewrite;
|
||||
seq_util m_util;
|
||||
arith_util m_autil;
|
||||
seq_skolem m_sk;
|
||||
seq_axioms m_ax;
|
||||
arith_value m_arith_value;
|
||||
th_trail_stack m_trail_stack;
|
||||
stats m_stats;
|
||||
symbol m_prefix, m_suffix, m_accept, m_reject;
|
||||
symbol m_tail, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step;
|
||||
symbol m_pre, m_post, m_eq, m_seq_align;
|
||||
ptr_vector<expr> m_todo, m_concat;
|
||||
unsigned m_internalize_depth;
|
||||
expr_ref_vector m_ls, m_rs, m_lhs, m_rhs;
|
||||
|
@ -588,16 +589,7 @@ namespace smt {
|
|||
bool is_var(expr* b) const;
|
||||
bool add_solution(expr* l, expr* r, dependency* dep);
|
||||
bool is_unit_nth(expr* a) const;
|
||||
bool is_tail(expr* a, expr*& s, unsigned& idx) const;
|
||||
bool is_tail_match(expr* a, expr*& s, expr*& idx) const;
|
||||
bool is_eq(expr* e, expr*& a, expr*& b) const;
|
||||
bool is_pre(expr* e, expr*& s, expr*& i);
|
||||
bool is_post(expr* e, expr*& s, expr*& i);
|
||||
expr_ref mk_post(expr* s, expr* i);
|
||||
expr_ref mk_sk_ite(expr* c, expr* t, expr* f);
|
||||
expr_ref mk_nth(expr* s, expr* idx);
|
||||
expr_ref mk_last(expr* e);
|
||||
expr_ref mk_first(expr* e);
|
||||
bool canonize(expr* e, dependency*& eqs, expr_ref& result);
|
||||
bool canonize(expr* e, expr_ref_vector& es, dependency*& eqs, bool& change);
|
||||
bool canonize(expr_ref_vector const& es, expr_ref_vector& result, dependency*& eqs, bool& change);
|
||||
|
@ -612,21 +604,8 @@ namespace smt {
|
|||
void deque_axiom(expr* e);
|
||||
void push_lit_as_expr(literal l, expr_ref_vector& buf);
|
||||
void add_axiom(literal l1, literal l2 = null_literal, literal l3 = null_literal, literal l4 = null_literal, literal l5 = null_literal);
|
||||
void add_indexof_axiom(expr* e);
|
||||
void add_last_indexof_axiom(expr* e);
|
||||
void add_replace_axiom(expr* e);
|
||||
void add_extract_axiom(expr* e);
|
||||
void add_length_axiom(expr* n);
|
||||
void add_tail_axiom(expr* e, expr* s);
|
||||
void add_drop_last_axiom(expr* e, expr* s);
|
||||
void add_extract_prefix_axiom(expr* e, expr* s, expr* l);
|
||||
void add_extract_suffix_axiom(expr* e, expr* s, expr* i);
|
||||
bool is_tail(expr* s, expr* i, expr* l);
|
||||
bool is_drop_last(expr* s, expr* i, expr* l);
|
||||
bool is_extract_prefix0(expr* s, expr* i, expr* l);
|
||||
bool is_extract_suffix(expr* s, expr* i, expr* l);
|
||||
|
||||
|
||||
bool has_length(expr *e) const { return m_has_length.contains(e); }
|
||||
void add_length(expr* e, expr* l);
|
||||
bool add_length_to_eqc(expr* n);
|
||||
|
@ -639,31 +618,21 @@ namespace smt {
|
|||
bool check_int_string(expr* e);
|
||||
|
||||
expr_ref add_elim_string_axiom(expr* n);
|
||||
void add_at_axiom(expr* n);
|
||||
void add_lt_axiom(expr* n);
|
||||
void add_le_axiom(expr* n);
|
||||
void add_unit_axiom(expr* n);
|
||||
void add_nth_axiom(expr* n);
|
||||
void add_in_re_axiom(expr* n);
|
||||
void add_itos_axiom(expr* n);
|
||||
void add_stoi_axiom(expr* n);
|
||||
bool add_stoi_val_axiom(expr* n);
|
||||
bool add_itos_val_axiom(expr* n);
|
||||
bool add_stoi_val_axiom(expr* n);
|
||||
void add_si_axiom(expr* s, expr* i, unsigned sz);
|
||||
void ensure_digit_axiom();
|
||||
literal is_digit(expr* ch);
|
||||
expr_ref digit2int(expr* ch);
|
||||
void add_itos_length_axiom(expr* n);
|
||||
literal mk_literal(expr* n);
|
||||
literal mk_simplified_literal(expr* n);
|
||||
literal mk_eq_empty(expr* n, bool phase = true);
|
||||
literal mk_seq_eq(expr* a, expr* b);
|
||||
literal mk_preferred_eq(expr* a, expr* b);
|
||||
void tightest_prefix(expr* s, expr* x);
|
||||
expr_ref mk_sub(expr* a, expr* b);
|
||||
expr_ref mk_add(expr* a, expr* b);
|
||||
expr_ref mk_len(expr* s);
|
||||
enode* ensure_enode(expr* a);
|
||||
ptr_vector<expr> m_ensure_todo;
|
||||
void ensure_enodes(expr* e);
|
||||
enode* get_root(expr* a) { return ensure_enode(a)->get_root(); }
|
||||
|
@ -679,8 +648,6 @@ namespace smt {
|
|||
bool get_length(expr* s, rational& val);
|
||||
|
||||
void mk_decompose(expr* e, expr_ref& head, expr_ref& tail);
|
||||
expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, expr* e4 = nullptr, sort* range = nullptr);
|
||||
bool is_skolem(symbol const& s, expr* e) const;
|
||||
|
||||
void set_incomplete(app* term);
|
||||
|
||||
|
@ -689,16 +656,8 @@ namespace smt {
|
|||
eautomaton* get_automaton(expr* e);
|
||||
literal mk_accept(expr* s, expr* idx, expr* re, expr* state);
|
||||
literal mk_accept(expr* s, expr* idx, expr* re, unsigned i) { return mk_accept(s, idx, re, m_autil.mk_int(i)); }
|
||||
bool is_accept(expr* acc) const { return is_skolem(m_accept, acc); }
|
||||
bool is_accept(expr* acc) const { return m_sk.is_accept(acc); }
|
||||
bool is_accept(expr* acc, expr*& s, expr*& idx, expr*& re, unsigned& i, eautomaton*& aut);
|
||||
expr_ref mk_step(expr* s, expr* tail, expr* re, unsigned i, unsigned j, expr* t);
|
||||
bool is_step(expr* e, expr*& s, expr*& tail, expr*& re, expr*& i, expr*& j, expr*& t) const;
|
||||
bool is_step(expr* e) const;
|
||||
bool is_max_unfolding(expr* e) const { return is_skolem(symbol("seq.max_unfolding_depth"), e); }
|
||||
expr_ref mk_max_unfolding_depth() {
|
||||
return mk_skolem(symbol("seq.max_unfolding_depth"), m_autil.mk_int(m_max_unfolding_depth),
|
||||
nullptr, nullptr, nullptr, m.mk_bool_sort());
|
||||
}
|
||||
void propagate_not_prefix(expr* e);
|
||||
void propagate_not_suffix(expr* e);
|
||||
void ensure_nth(literal lit, expr* s, expr* idx);
|
||||
|
|
Loading…
Reference in a new issue