mirror of
https://github.com/Z3Prover/z3
synced 2025-08-11 13:40:52 +00:00
move to separate axiom management
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
9bde93f812
commit
377d060036
16 changed files with 302 additions and 565 deletions
|
@ -69,6 +69,15 @@ func_decl* char_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,
|
|||
return m.mk_func_decl(symbol("char.to_int"), arity, domain, a.mk_int(), func_decl_info(m_family_id, k, 0, nullptr));
|
||||
}
|
||||
m.raise_exception(msg.str());
|
||||
case OP_CHAR_IS_DIGIT:
|
||||
if (num_parameters != 0)
|
||||
msg << "incorrect number of parameters passed. Expected 0, received " << num_parameters;
|
||||
else if (arity != 1)
|
||||
msg << "incorrect number of arguments passed. Expected one character, received " << arity;
|
||||
else {
|
||||
return m.mk_func_decl(symbol("char.is_digit"), arity, domain, m.mk_bool_sort(), func_decl_info(m_family_id, k, 0, nullptr));
|
||||
}
|
||||
m.raise_exception(msg.str());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -85,6 +94,7 @@ void char_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol cons
|
|||
op_names.push_back(builtin_name("char.<=", OP_CHAR_LE));
|
||||
op_names.push_back(builtin_name("Char", OP_CHAR_CONST));
|
||||
op_names.push_back(builtin_name("char.to_int", OP_CHAR_TO_INT));
|
||||
op_names.push_back(builtin_name("char.is_digit", OP_CHAR_IS_DIGIT));
|
||||
}
|
||||
|
||||
void char_decl_plugin::get_sort_names(svector<builtin_name>& sort_names, symbol const& logic) {
|
||||
|
|
|
@ -33,7 +33,8 @@ enum char_sort_kind {
|
|||
enum char_op_kind {
|
||||
OP_CHAR_CONST,
|
||||
OP_CHAR_LE,
|
||||
OP_CHAR_TO_INT
|
||||
OP_CHAR_TO_INT,
|
||||
OP_CHAR_IS_DIGIT
|
||||
};
|
||||
|
||||
class char_decl_plugin : public decl_plugin {
|
||||
|
@ -79,6 +80,8 @@ public:
|
|||
|
||||
app* mk_to_int(expr* a) { return m_manager->mk_app(m_family_id, OP_CHAR_TO_INT, 1, &a); }
|
||||
|
||||
app* mk_is_digit(expr* a) { return m_manager->mk_app(m_family_id, OP_CHAR_IS_DIGIT, 1, &a); }
|
||||
|
||||
bool is_le(expr const* e) const { return is_app_of(e, m_family_id, OP_CHAR_LE); }
|
||||
|
||||
bool is_const_char(expr const* e, unsigned& c) const {
|
||||
|
@ -87,6 +90,12 @@ public:
|
|||
|
||||
bool is_to_int(expr const* e) const { return is_app_of(e, m_family_id, OP_CHAR_TO_INT); }
|
||||
|
||||
bool is_is_digit(expr const* e) const { return is_app_of(e, m_family_id, OP_CHAR_IS_DIGIT); }
|
||||
|
||||
MATCH_UNARY(is_is_digit);
|
||||
MATCH_UNARY(is_to_int);
|
||||
MATCH_BINARY(is_le);
|
||||
|
||||
bool unicode() const { return m_unicode; }
|
||||
unsigned max_char() const { return m_unicode ? zstring::unicode_max_char() : zstring::ascii_max_char(); }
|
||||
unsigned num_bits() const { return m_unicode ? zstring::unicode_num_bits() : zstring::ascii_num_bits(); }
|
||||
|
|
|
@ -41,6 +41,30 @@ namespace seq {
|
|||
return result;
|
||||
}
|
||||
|
||||
expr_ref axioms::mk_ge_e(expr* x, expr* y) {
|
||||
expr_ref ge(a.mk_ge(x, y), m);
|
||||
m_rewrite(ge);
|
||||
return ge;
|
||||
}
|
||||
|
||||
expr_ref axioms::mk_le_e(expr* x, expr* y) {
|
||||
expr_ref le(a.mk_le(x, y), m);
|
||||
m_rewrite(le);
|
||||
return le;
|
||||
}
|
||||
|
||||
expr_ref axioms::mk_seq_eq(expr* a, expr* b) {
|
||||
SASSERT(seq.is_seq(a) && seq.is_seq(b));
|
||||
expr_ref result(m_sk.mk_eq(a, b), m);
|
||||
m_set_phase(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
expr_ref axioms::mk_eq_empty(expr* e) {
|
||||
return mk_seq_eq(seq.str.mk_empty(e->get_sort()), e);
|
||||
}
|
||||
|
||||
expr_ref axioms::purify(expr* e) {
|
||||
if (!e)
|
||||
return expr_ref(m);
|
||||
|
@ -48,7 +72,12 @@ namespace seq {
|
|||
return expr_ref(e, m);
|
||||
if (m.is_value(e))
|
||||
return expr_ref(e, m);
|
||||
expr_ref p(m);
|
||||
|
||||
expr_ref p(e, m);
|
||||
m_rewrite(p);
|
||||
if (p == e)
|
||||
return p;
|
||||
|
||||
expr* r = nullptr;
|
||||
if (m_purified.find(e, r))
|
||||
p = r;
|
||||
|
@ -152,6 +181,9 @@ namespace seq {
|
|||
auto s = purify(_s);
|
||||
auto i = purify(_i);
|
||||
auto l = purify(_l);
|
||||
if (small_segment_axiom(e, _s, _i, _l))
|
||||
return;
|
||||
|
||||
if (is_tail(s, _i, _l)) {
|
||||
tail_axiom(e, s);
|
||||
return;
|
||||
|
@ -237,11 +269,24 @@ namespace seq {
|
|||
return l1 == l2;
|
||||
}
|
||||
|
||||
bool axioms::small_segment_axiom(expr* s, expr* e, expr* i, expr* l) {
|
||||
rational ln;
|
||||
if (a.is_numeral(i, ln) && ln >= 0 && a.is_numeral(l, ln) && ln <= 5) {
|
||||
expr_ref_vector es(m);
|
||||
for (unsigned j = 0; j < ln; ++j)
|
||||
es.push_back(seq.str.mk_at(e, a.mk_add(i, a.mk_int(j))));
|
||||
expr_ref r(seq.str.mk_concat(es, e->get_sort()), m);
|
||||
add_clause(mk_seq_eq(r, s));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool axioms::is_tail(expr* s, expr* i, expr* l) {
|
||||
rational i1;
|
||||
if (!a.is_numeral(i, i1) || !i1.is_one()) {
|
||||
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);
|
||||
|
@ -617,7 +662,9 @@ namespace seq {
|
|||
|
||||
// n >= 0 => stoi(itos(n)) = n
|
||||
app_ref stoi(seq.str.mk_stoi(e), m);
|
||||
add_clause(~ge0, mk_eq(stoi, n));
|
||||
expr_ref eq = mk_eq(stoi, n);
|
||||
add_clause(~ge0, eq);
|
||||
m_set_phase(eq);
|
||||
|
||||
// itos(n) does not start with "0" when n > 0
|
||||
// n = 0 or at(itos(n),0) != "0"
|
||||
|
@ -750,17 +797,12 @@ namespace seq {
|
|||
}
|
||||
|
||||
expr_ref axioms::is_digit(expr* ch) {
|
||||
expr_ref isd = expr_ref(m_sk.mk_is_digit(ch), m);
|
||||
expr_ref lo(seq.mk_le(seq.mk_char('0'), ch), m);
|
||||
expr_ref hi(seq.mk_le(ch, seq.mk_char('9')), m);
|
||||
add_clause(~lo, ~hi, isd);
|
||||
add_clause(~isd, lo);
|
||||
add_clause(~isd, hi);
|
||||
return isd;
|
||||
return expr_ref(seq.mk_char_is_digit(ch), m);
|
||||
}
|
||||
|
||||
expr_ref axioms::mk_digit2int(expr* ch) {
|
||||
return expr_ref(a.mk_add(seq.mk_char2int(ch), a.mk_int(-((int)'0'))), m);;
|
||||
m_ensure_digits();
|
||||
return expr_ref(m_sk.mk_digit2int(ch), m);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,6 +36,8 @@ namespace seq {
|
|||
expr_ref_vector m_trail;
|
||||
obj_map<expr, expr*> m_purified;
|
||||
std::function<void(expr_ref_vector const&)> m_add_clause;
|
||||
std::function<void(expr*)> m_set_phase;
|
||||
std::function<void(void)> m_ensure_digits;
|
||||
|
||||
expr_ref mk_len(expr* s);
|
||||
expr_ref mk_sub(expr* x, expr* y);
|
||||
|
@ -43,17 +45,19 @@ namespace seq {
|
|||
expr_ref mk_concat(expr* e1, expr* e2) { return expr_ref(seq.str.mk_concat(e1, e2), m); }
|
||||
expr_ref mk_nth(expr* e, unsigned i) { return expr_ref(seq.str.mk_nth_i(e, a.mk_int(i)), m); }
|
||||
expr_ref mk_eq(expr* a, expr* b) { return expr_ref(m.mk_eq(a, b), m); }
|
||||
expr_ref mk_seq_eq(expr* a, expr* b) { SASSERT(seq.is_seq(a) && seq.is_seq(b)); return expr_ref(m_sk.mk_eq(a, b), m); }
|
||||
expr_ref mk_eq_empty(expr* e) { return expr_ref(m.mk_eq(seq.str.mk_empty(e->get_sort()), e), m); }
|
||||
expr_ref mk_seq_eq(expr* a, expr* b);
|
||||
expr_ref mk_eq_empty(expr* e);
|
||||
|
||||
expr_ref mk_ge(expr* x, unsigned n) { return expr_ref(a.mk_ge(x, a.mk_int(n)), m); }
|
||||
expr_ref mk_le(expr* x, unsigned n) { return expr_ref(a.mk_le(x, a.mk_int(n)), m); }
|
||||
expr_ref mk_ge(expr* x, rational const& n) { return expr_ref(a.mk_ge(x, a.mk_int(n)), m); }
|
||||
expr_ref mk_le(expr* x, rational const& n) { return expr_ref(a.mk_le(x, a.mk_int(n)), m); }
|
||||
expr_ref mk_ge(expr* x, int n) { return mk_ge_e(x, a.mk_int(n)); }
|
||||
expr_ref mk_le(expr* x, int n) { return mk_le_e(x, a.mk_int(n)); }
|
||||
expr_ref mk_ge(expr* x, rational const& n) { return mk_ge_e(x, a.mk_int(n)); }
|
||||
expr_ref mk_le(expr* x, rational const& n) { return mk_le_e(x, a.mk_int(n)); }
|
||||
|
||||
expr_ref mk_ge_e(expr* x, expr* y);
|
||||
expr_ref mk_le_e(expr* x, expr* y);
|
||||
|
||||
void gc_purify();
|
||||
|
||||
expr_ref is_digit(expr* ch);
|
||||
expr_ref purify(expr* e);
|
||||
expr_ref mk_digit2int(expr* ch);
|
||||
|
||||
|
@ -70,6 +74,7 @@ namespace seq {
|
|||
|
||||
void tail_axiom(expr* e, expr* s);
|
||||
void drop_last_axiom(expr* e, expr* s);
|
||||
bool small_segment_axiom(expr* e, expr* s, expr* i, expr* l);
|
||||
void extract_prefix_axiom(expr* e, expr* s, expr* l);
|
||||
void extract_suffix_axiom(expr* e, expr* s, expr* l);
|
||||
void tightest_prefix(expr* s, expr* x);
|
||||
|
@ -79,6 +84,8 @@ namespace seq {
|
|||
axioms(th_rewriter& rw);
|
||||
|
||||
void set_add_clause(std::function<void(expr_ref_vector const&)>& ac) { m_add_clause = ac; }
|
||||
void set_phase(std::function<void(expr*)>& sp) { m_set_phase = sp; }
|
||||
void set_ensure_digits(std::function<void(void)>& ed) { m_ensure_digits = ed; }
|
||||
|
||||
void suffix_axiom(expr* n);
|
||||
void prefix_axiom(expr* n);
|
||||
|
@ -102,6 +109,8 @@ namespace seq {
|
|||
void unroll_not_contains(expr* e);
|
||||
|
||||
expr_ref length_limit(expr* s, unsigned k);
|
||||
expr_ref is_digit(expr* ch);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -833,6 +833,11 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
|
|||
result = m_autil.mk_add(es.size(), es.c_ptr());
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
expr* x = nullptr, *y = nullptr, *z = nullptr;
|
||||
if (str().is_replace(a, x, y, z) && l_true == eq_length(y, z)) {
|
||||
result = str().mk_length(x);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
#if 0
|
||||
expr* s = nullptr, *offset = nullptr, *length = nullptr;
|
||||
if (str().is_extract(a, s, offset, length)) {
|
||||
|
@ -1147,12 +1152,23 @@ br_status seq_rewriter::mk_seq_extract(expr* a, expr* b, expr* c, expr_ref& resu
|
|||
return BR_REWRITE3;
|
||||
}
|
||||
|
||||
if (str().is_extract(a, a1, b1, c1) &&
|
||||
is_prefix(a, b, c) && is_suffix(a1, b1, c1)) {
|
||||
expr_ref q(m_autil.mk_sub(c, str().mk_length(a)), m());
|
||||
std::cout << "prefix-suffix " << mk_pp(a, m()) << " " << mk_pp(b, m()) << " " << mk_pp(c, m()) << "\n";
|
||||
std::cout << q << "\n";
|
||||
result = str().mk_substr(a1, b1, m_autil.mk_add(c1, q));
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
|
||||
|
||||
// extract(extract(a, 3, 6), 1, len(extract(a, 3, 6)) - 1) -> extract(a, 4, 5)
|
||||
if (str().is_extract(a, a1, b1, c1) && is_suffix(a, b, c) &&
|
||||
m_autil.is_numeral(c1) && m_autil.is_numeral(b1)) {
|
||||
result = str().mk_substr(a1, m_autil.mk_add(b, b1), m_autil.mk_sub(c1, b));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
|
||||
if (!constantPos)
|
||||
return BR_FAILED;
|
||||
|
@ -1365,11 +1381,51 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) {
|
|||
result = ::mk_or(ors);
|
||||
return BR_REWRITE_FULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
expr_ref ra(a, m());
|
||||
if (is_unit(b) && m().is_value(b) &&
|
||||
reduce_by_char(ra, b, 4)) {
|
||||
result = str().mk_contains(ra, b);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool seq_rewriter::reduce_by_char(expr_ref& r, expr* ch, unsigned depth) {
|
||||
expr* x = nullptr, *y = nullptr, *z = nullptr;
|
||||
if (str().is_replace(r, x, y, z) &&
|
||||
str().is_unit(y) && m().is_value(y) &&
|
||||
str().is_unit(z) && m().is_value(z) &&
|
||||
ch != y && ch != z) {
|
||||
r = x;
|
||||
if (depth > 0)
|
||||
reduce_by_char(r, ch, depth - 1);
|
||||
return true;
|
||||
}
|
||||
if (depth > 0 && str().is_concat(r)) {
|
||||
bool reduced = false;
|
||||
expr_ref_vector args(m());
|
||||
for (expr* e : *to_app(r)) {
|
||||
expr_ref tmp(e, m());
|
||||
if (reduce_by_char(tmp, ch, depth - 1))
|
||||
reduced = true;
|
||||
args.push_back(tmp);
|
||||
}
|
||||
if (reduced)
|
||||
r = str().mk_concat(args, args.get(0)->get_sort());
|
||||
return reduced;
|
||||
}
|
||||
if (depth > 0 && str().is_extract(r, x, y, z)) {
|
||||
expr_ref tmp(x, m());
|
||||
if (reduce_by_char(tmp, ch, depth - 1)) {
|
||||
r = str().mk_substr(tmp, y, z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (str.at s i), constants s/i, i < 0 or i >= |s| ==> (str.at s i) = ""
|
||||
*/
|
||||
|
@ -1632,6 +1688,13 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
|
|||
m().mk_ite(m_autil.mk_ge(b1, zero()), m_autil.mk_add(one(), b1), minus_one()));
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
expr_ref ra(a, m());
|
||||
if (str().is_unit(b) && m().is_value(b) &&
|
||||
reduce_by_char(ra, b, 4)) {
|
||||
result = str().mk_index(ra, b, c);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
|
||||
// Enhancement: walk segments of a, determine which segments must overlap, must not overlap, may overlap.
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
@ -1953,6 +2016,16 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) {
|
|||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
unsigned len_a;
|
||||
rational len_b;
|
||||
if (max_length(b, len_b)) {
|
||||
min_length(a, len_a);
|
||||
if (len_b <= len_a) {
|
||||
result = m().mk_eq(a, b);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
@ -2021,7 +2094,15 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) {
|
|||
result = str().mk_suffix(a1, b);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
unsigned len_a;
|
||||
rational len_b;
|
||||
if (max_length(b, len_b)) {
|
||||
min_length(a, len_a);
|
||||
if (len_b <= len_a) {
|
||||
result = m().mk_eq(a, b);
|
||||
return BR_REWRITE1;
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
@ -4590,6 +4671,13 @@ bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_pa
|
|||
return true;
|
||||
}
|
||||
|
||||
lbool seq_rewriter::eq_length(expr* x, expr* y) {
|
||||
unsigned xl = 0, yl = 0;
|
||||
if (min_length(x, xl) && min_length(y, yl))
|
||||
return xl == yl ? l_true : l_false;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
/***
|
||||
\brief extract the minimal length of the sequence.
|
||||
Return true if the minimal length is equal to the
|
||||
|
|
|
@ -289,7 +289,9 @@ class seq_rewriter {
|
|||
bool min_length(expr_ref_vector const& es, unsigned& len);
|
||||
bool min_length(expr* e, unsigned& len);
|
||||
bool max_length(expr* e, rational& len);
|
||||
lbool eq_length(expr* x, expr* y);
|
||||
expr* concat_non_empty(expr_ref_vector& es);
|
||||
bool reduce_by_char(expr_ref& r, expr* ch, unsigned depth);
|
||||
|
||||
bool is_string(unsigned n, expr* const* es, zstring& s) const;
|
||||
|
||||
|
|
|
@ -133,6 +133,8 @@ namespace seq {
|
|||
a.is_unsigned(i, idx);
|
||||
}
|
||||
bool is_align(expr const* e) const { return is_skolem(symbol("seq.align.m"), e); }
|
||||
bool is_align_l(expr const* e) const { return is_skolem(symbol("seq.align.l"), e); }
|
||||
bool is_align_r(expr const* e) const { return is_skolem(symbol("seq.align.r"), e); }
|
||||
MATCH_BINARY(is_align);
|
||||
bool is_post(expr* e, expr*& s, expr*& start);
|
||||
bool is_pre(expr* e, expr*& s, expr*& i);
|
||||
|
|
|
@ -234,9 +234,12 @@ public:
|
|||
bool is_const_char(expr* e, unsigned& c) const;
|
||||
bool is_const_char(expr* e) const { unsigned c; return is_const_char(e, c); }
|
||||
bool is_char_le(expr const* e) const;
|
||||
bool is_char_is_digit(expr const* e, expr*& d) const { return ch.is_is_digit(e, d); }
|
||||
bool is_char_is_digit(expr const* e) const { return ch.is_is_digit(e); }
|
||||
bool is_char2int(expr const* e) const;
|
||||
app* mk_char_bit(expr* e, unsigned i);
|
||||
app* mk_char(unsigned ch) const;
|
||||
app* mk_char_is_digit(expr* e) { return ch.mk_is_digit(e); }
|
||||
app* mk_le(expr* ch1, expr* ch2) const;
|
||||
app* mk_lt(expr* ch1, expr* ch2) const;
|
||||
app* mk_char2int(expr* e) { return ch.mk_to_int(e); }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue