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

Merge branch 'master' of https://github.com/z3prover/z3 into polysat

This commit is contained in:
Nikolaj Bjorner 2021-08-11 09:21:58 -07:00
commit 524ebed44f
14 changed files with 361 additions and 102 deletions

View file

@ -285,6 +285,7 @@ void OP(unsigned num_args, expr * const * args, expr_ref & result) { \
MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder); MK_BIN_AC_REDUCE(reduce_add, reduce_bin_add, mk_adder);
MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier); MK_BIN_AC_REDUCE(reduce_mul, reduce_bin_mul, mk_multiplier);
MK_BIN_AC_REDUCE(reduce_and, reduce_bin_and, mk_and);
MK_BIN_AC_REDUCE(reduce_or, reduce_bin_or, mk_or); MK_BIN_AC_REDUCE(reduce_or, reduce_bin_or, mk_or);
MK_BIN_AC_REDUCE(reduce_xor, reduce_bin_xor, mk_xor); MK_BIN_AC_REDUCE(reduce_xor, reduce_bin_xor, mk_xor);
@ -357,8 +358,11 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
result = mk_mkbv(m_out); result = mk_mkbv(m_out);
} }
void throw_unsupported() { void throw_unsupported(func_decl * f) {
throw rewriter_exception("operator is not supported, you must simplify the goal before applying bit-blasting"); std::string msg = "operator ";
msg += f->get_name().str();
msg += " is not supported, you must simplify the goal before applying bit-blasting";
throw rewriter_exception(std::move(msg));
} }
void blast_bv_term(expr * t, expr_ref & result, proof_ref & result_pr) { void blast_bv_term(expr * t, expr_ref & result, proof_ref & result_pr) {
@ -424,7 +428,7 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
case OP_BUREM: case OP_BUREM:
case OP_BSMOD: case OP_BSMOD:
if (m_blast_mul) if (m_blast_mul)
throw_unsupported(); // must simplify to DIV_I AND DIV0 throw_unsupported(f); // must simplify to DIV_I AND DIV0
return BR_FAILED; // keep them return BR_FAILED; // keep them
case OP_BSDIV0: case OP_BSDIV0:
@ -472,6 +476,9 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
SASSERT(num == 2); SASSERT(num == 2);
reduce_sle(args[0], args[1], result); reduce_sle(args[0], args[1], result);
return BR_DONE; return BR_DONE;
case OP_BAND:
reduce_and(num, args, result);
return BR_DONE;
case OP_BOR: case OP_BOR:
reduce_or(num, args, result); reduce_or(num, args, result);
return BR_DONE; return BR_DONE;
@ -545,7 +552,7 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend);
default: default:
TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n"; TRACE("bit_blaster", tout << "non-supported operator: " << f->get_name() << "\n";
for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;); for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << std::endl;);
throw_unsupported(); throw_unsupported(f);
} }
} }

View file

@ -1043,12 +1043,19 @@ namespace seq {
} }
/** /**
suffix(s, t):
- the sequence s is a suffix of t.
suffix(s, t) => s = seq.suffix_inv(s, t) + t Positive case is handled by the solver when the atom is asserted
suffix(s, t) => t = seq.suffix_inv(s, t) + s
Negative case is handled by axioms when the negation of the atom is asserted
~suffix(s, t) => len(s) > len(t) or s = y(s, t) + unit(c(s, t)) + x(s, t) ~suffix(s, t) => len(s) > len(t) or s = y(s, t) + unit(c(s, t)) + x(s, t)
~suffix(s, t) => len(s) > len(t) or t = z(s, t) + unit(d(s, t)) + x(s, t) ~suffix(s, t) => len(s) > len(t) or t = z(s, t) + unit(d(s, t)) + x(s, t)
~suffix(s, t) => len(s) > len(t) or c(s,t) != d(s,t) ~suffix(s, t) => len(s) > len(t) or c(s,t) != d(s,t)
Symmetric axioms are provided for prefix
*/ */
void axioms::suffix_axiom(expr* e) { void axioms::suffix_axiom(expr* e) {
@ -1101,7 +1108,7 @@ namespace seq {
expr_ref c = m_sk.mk("seq.prefix.c", s, t, char_sort); expr_ref c = m_sk.mk("seq.prefix.c", s, t, char_sort);
expr_ref d = m_sk.mk("seq.prefix.d", s, t, char_sort); expr_ref d = m_sk.mk("seq.prefix.d", s, t, char_sort);
add_clause(lit, s_gt_t, mk_seq_eq(s, mk_concat(x, seq.str.mk_unit(c), y))); add_clause(lit, s_gt_t, mk_seq_eq(s, mk_concat(x, seq.str.mk_unit(c), y)));
add_clause(lit, s_gt_t, mk_seq_eq(t, mk_concat(x, seq.str.mk_unit(d), z)), mk_seq_eq(t, x)); add_clause(lit, s_gt_t, mk_seq_eq(t, mk_concat(x, seq.str.mk_unit(d), z)));
add_clause(lit, s_gt_t, ~mk_eq(c, d)); add_clause(lit, s_gt_t, ~mk_eq(c, d));
#endif #endif
} }

View file

@ -14,6 +14,7 @@ Author:
Nikolaj Bjorner (nbjorner) 2015-12-5 Nikolaj Bjorner (nbjorner) 2015-12-5
Murphy Berzish 2017-02-21 Murphy Berzish 2017-02-21
Caleb Stanford 2020-07-07 Caleb Stanford 2020-07-07
Margus Veanes 2021
--*/ --*/
@ -872,6 +873,65 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
return BR_FAILED; return BR_FAILED;
} }
/*
* In general constructs nth(t,0) but if t = substring(s,j,..) then simplifies to nth(s,j)
* This method assumes that |t| > 0.
*/
expr_ref seq_rewriter::mk_seq_first(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
if (str().is_extract(t, s, j, k))
result = str().mk_nth_i(s, j);
else
result = str().mk_nth_i(t, m_autil.mk_int(0));
return result;
}
/*
* In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1)
* This method assumes that |t| > 0.
*/
expr_ref seq_rewriter::mk_seq_rest(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m());
if (str().is_extract(t, s, j, k))
result = str().mk_substr(s, m_autil.mk_add(j, one), m_autil.mk_sub(k, one));
else
result = str().mk_substr(t, one, m_autil.mk_sub(str().mk_length(t), one));
return result;
}
/*
* In general constructs nth(t,|t|-1) but if t = substring(s,j,k) then simplifies to nth(s,j+k-1)
* This method assumes that |t| > 0.
*/
expr_ref seq_rewriter::mk_seq_last(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m());
if (str().is_extract(t, s, j, k))
result = str().mk_nth_i(s, m_autil.mk_sub(m_autil.mk_add(j, k), one));
else
result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one));
return result;
}
/*
* In general constructs substring(t,0,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j,k-1)
* This method assumes that |t| > 0 holds.
*/
expr_ref seq_rewriter::mk_seq_butlast(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m());
if (str().is_extract(t, s, j, k))
result = str().mk_substr(s, j, m_autil.mk_sub(k, one));
else
result = str().mk_substr(t, m_autil.mk_int(0), m_autil.mk_sub(str().mk_length(t), one));
return result;
}
/* /*
Lift all ite expressions to the top level, safely Lift all ite expressions to the top level, safely
throttled to not blowup the size of the expression. throttled to not blowup the size of the expression.
@ -1890,16 +1950,16 @@ br_status seq_rewriter::mk_seq_replace_all(expr* a, expr* b, expr* c, expr_ref&
} }
if (a == b) { if (a == b) {
result = m().mk_ite(str().mk_is_empty(b), str().mk_empty(a->get_sort()), c); result = m().mk_ite(str().mk_is_empty(b), str().mk_empty(a->get_sort()), c);
return BR_REWRITE1; return BR_REWRITE2;
} }
zstring s1, s2; zstring s1, s2;
expr_ref_vector strs(m());
if (str().is_string(a, s1) && str().is_string(b, s2)) { if (str().is_string(a, s1) && str().is_string(b, s2)) {
SASSERT(s2.length() > 0); SASSERT(s2.length() > 0);
if (s1.length() < s2.length()) { if (s1.length() < s2.length()) {
result = a; result = a;
return BR_DONE; return BR_DONE;
} }
expr_ref_vector strs(m());
for (unsigned i = 0; i < s1.length(); ++i) { for (unsigned i = 0; i < s1.length(); ++i) {
if (s1.length() >= s2.length() + i && if (s1.length() >= s2.length() + i &&
s2 == s1.extract(i, s2.length())) { s2 == s1.extract(i, s2.length())) {
@ -1912,11 +1972,63 @@ br_status seq_rewriter::mk_seq_replace_all(expr* a, expr* b, expr* c, expr_ref&
result = str().mk_concat(strs, a->get_sort()); result = str().mk_concat(strs, a->get_sort());
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
// TBD: add case when a, b are concatenation of units that are values. expr_ref_vector a_vals(m());
// in this case we can use a similar loop as for strings. expr_ref_vector b_vals(m());
if (try_get_unit_values(a, a_vals) && try_get_unit_values(b, b_vals)) {
replace_all_subvectors(a_vals, b_vals, c, strs);
result = str().mk_concat(strs, a->get_sort());
return BR_REWRITE_FULL;
}
//TODO: the case when a is a unit or concatenation of units while b is a string
//or the other way around -- if that situation is possible at all -- is similar to the above
return BR_FAILED; return BR_FAILED;
} }
/*
* Returns false if s is not a single unit value or concatenation of unit values.
* Else extracts the units from s into vals and returns true.
*/
bool seq_rewriter::try_get_unit_values(expr* s, expr_ref_vector& vals) {
expr* h, * t, * v;
t = s;
//collect all unit values from s, if not all elements are unit-values then fail
while (str().is_concat(t, h, t))
if (str().is_unit(h, v) && m().is_value(v))
vals.push_back(h);
else
return false;
//add the last element
if (str().is_unit(t, v) && m().is_value(v))
vals.push_back(t);
else
return false;
return true;
}
/*
* Replace all subvectors of b in a by c
*/
void seq_rewriter::replace_all_subvectors(expr_ref_vector const& a, expr_ref_vector const& b, expr* c, expr_ref_vector& result) {
unsigned int i = 0;
unsigned int k = b.size();
while (i + k <= a.size()) {
//if a[i..i+k-1] equals b then replace it by c and inceremnt i by k
unsigned j = 0;
while (j < k && b[j] == a[i + j])
++j;
if (j < k) //the equality failed
result.push_back(a[i++]);
else { //the equality succeeded
result.push_back(c);
i += k;
}
}
//add the trailing elements from a
while (i < a.size())
result.push_back(a[i++]);
}
br_status seq_rewriter::mk_seq_replace_re_all(expr* a, expr* b, expr* c, expr_ref& result) { br_status seq_rewriter::mk_seq_replace_re_all(expr* a, expr* b, expr* c, expr_ref& result) {
return BR_FAILED; return BR_FAILED;
} }
@ -2521,12 +2633,18 @@ bool seq_rewriter::get_re_head_tail_reversed(expr* r, expr_ref& head, expr_ref&
if (re().is_concat(r, r1, r2)) { if (re().is_concat(r, r1, r2)) {
unsigned len = re().min_length(r2); unsigned len = re().min_length(r2);
if (len != UINT_MAX && re().max_length(r2) == len) { if (len != UINT_MAX && re().max_length(r2) == len) {
if (get_re_head_tail_reversed(r1, head, tail))
// left associative binding of concat
tail = mk_re_append(tail, r2);
else {
// right associative binding of concat
head = r1; head = r1;
tail = r2; tail = r2;
}
return true; return true;
} }
if (get_re_head_tail_reversed(r2, head, tail)) { if (get_re_head_tail_reversed(r2, head, tail)) {
head = re().mk_concat(r1, head); head = mk_re_append(r1, head);
return true; return true;
} }
} }
@ -3059,7 +3177,8 @@ expr_ref seq_rewriter::mk_der_op_rec(decl_kind k, expr* a, expr* b) {
return result; return result;
} }
// Order with higher IDs on the outside // Order with higher IDs on the outside
if (get_id(ca) < get_id(cb)) { bool is_symmetric = k == OP_RE_UNION || k == OP_RE_INTERSECT;
if (is_symmetric && get_id(ca) < get_id(cb)) {
std::swap(a, b); std::swap(a, b);
std::swap(ca, cb); std::swap(ca, cb);
std::swap(notca, notcb); std::swap(notca, notcb);
@ -3348,7 +3467,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
//while mk_empty() = [], because deriv(epsilon) = [] = nothing //while mk_empty() = [], because deriv(epsilon) = [] = nothing
return mk_empty(); return mk_empty();
} }
else if (str().is_itos(r1, r2)) { else if (str().is_itos(r1)) {
// //
// here r1 = (str.from_int r2) and r2 is non-ground // here r1 = (str.from_int r2) and r2 is non-ground
// or else the expression would have been simplified earlier // or else the expression would have been simplified earlier
@ -3356,16 +3475,24 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
// '0' <= elem <= '9' // '0' <= elem <= '9'
// if ((isdigit ele) and (ele = (hd r1))) then (to_re (tl r1)) else [] // if ((isdigit ele) and (ele = (hd r1))) then (to_re (tl r1)) else []
// //
hd = str().mk_nth_i(r1, m_autil.mk_int(0)); hd = mk_seq_first(r1);
m_br.mk_and(u().mk_le(m_util.mk_char('0'), ele), u().mk_le(ele, m_util.mk_char('9')), m().mk_eq(hd, ele), result); m_br.mk_and(u().mk_le(m_util.mk_char('0'), ele), u().mk_le(ele, m_util.mk_char('9')),
tl = re().mk_to_re(str().mk_substr(r1, m_autil.mk_int(1), m_autil.mk_sub(str().mk_length(r1), m_autil.mk_int(1)))); m().mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(hd, ele)), result);
tl = re().mk_to_re(mk_seq_rest(r1));
return re_and(result, tl);
}
else {
// recall: [] denotes the empty language (nothing) regex, () denotes epsilon or empty sequence
// construct the term (if (r1 != () and (ele = (first r1)) then (to_re (rest r1)) else []))
hd = mk_seq_first(r1);
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result);
tl = re().mk_to_re(mk_seq_rest(r1));
return re_and(result, tl); return re_and(result, tl);
} }
} }
else if (re().is_reverse(r, r1) && re().is_to_re(r1, r2)) { else if (re().is_reverse(r, r1)) {
// Reverses are rewritten so that the only derivative case is if (re().is_to_re(r1, r2)) {
// derivative of a reverse of a string. (All other cases stuck) // First try to exctract hd and tl such that r = hd ++ tl and |tl|=1
// This is analagous to the previous is_to_re case.
expr_ref hd(m()), tl(m()); expr_ref hd(m()), tl(m());
if (get_head_tail_reversed(r2, hd, tl)) { if (get_head_tail_reversed(r2, hd, tl)) {
// Use mk_der_cond to normalize // Use mk_der_cond to normalize
@ -3378,6 +3505,17 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
else if (str().is_empty(r2)) { else if (str().is_empty(r2)) {
return mk_empty(); return mk_empty();
} }
else {
// construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else []))
// hd = first of reverse(r2) i.e. last of r2
// tl = rest of reverse(r2) i.e. butlast of r2
//hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), m_autil.mk_int(1)));
hd = mk_seq_last(r2);
m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result);
tl = re().mk_to_re(mk_seq_butlast(r2));
return re_and(result, re().mk_reverse(tl));
}
}
} }
else if (re().is_range(r, r1, r2)) { else if (re().is_range(r, r1, r2)) {
// r1, r2 are sequences. // r1, r2 are sequences.
@ -3423,10 +3561,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
STRACE("seq_verbose", tout << "deriv of_pred" << std::endl;); STRACE("seq_verbose", tout << "deriv of_pred" << std::endl;);
return mk_der_cond(result, ele, seq_sort); return mk_der_cond(result, ele, seq_sort);
} }
// stuck cases: re.derivative, variable, // stuck cases: re.derivative, re variable,
// str.to_re if the head of the string can't be obtained,
// and re.reverse if not applied to a string or if the tail char
// of the string can't be obtained
return expr_ref(re().mk_derivative(ele, r), m()); return expr_ref(re().mk_derivative(ele, r), m());
} }
@ -3665,9 +3800,11 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref len_tl(m_autil.mk_int(re().min_length(tl)), m()); expr_ref len_tl(m_autil.mk_int(re().min_length(tl)), m());
expr_ref len_a(str().mk_length(a), m()); expr_ref len_a(str().mk_length(a), m());
expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m()); expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m());
expr* s = nullptr;
result = m().mk_and(m_autil.mk_ge(len_a, len_tl), result = m().mk_and(m_autil.mk_ge(len_a, len_tl),
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd), re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)); (re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) :
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)));
return BR_REWRITE_FULL; return BR_REWRITE_FULL;
} }
@ -4974,6 +5111,12 @@ bool seq_rewriter::reduce_itos(expr_ref_vector& ls, expr_ref_vector& rs,
str().is_itos(ls.get(0), n) && str().is_itos(ls.get(0), n) &&
is_string(rs.size(), rs.data(), s)) { is_string(rs.size(), rs.data(), s)) {
std::string s1 = s.encode(); std::string s1 = s.encode();
for (auto c : s1) {
if (!('0' <= c && c <= '9'))
return false;
}
if (s1.size() > 1 && s1[0] == '0')
return false;
try { try {
rational r(s1.c_str()); rational r(s1.c_str());
if (s1 == r.to_string()) { if (s1 == r.to_string()) {
@ -5015,6 +5158,10 @@ bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) {
result = m().mk_or(fmls); result = m().mk_or(fmls);
return true; return true;
} }
if (str().is_itos(r, s)) {
result = m_autil.mk_lt(s, m_autil.mk_int(0));
return true;
}
return false; return false;
} }

View file

@ -180,6 +180,16 @@ class seq_rewriter {
expr_ref mk_seq_concat(expr* a, expr* b); expr_ref mk_seq_concat(expr* a, expr* b);
// Construct the expressions for taking the first element, the last element, the rest, and the butlast element
expr_ref mk_seq_first(expr* s);
expr_ref mk_seq_rest(expr* s);
expr_ref mk_seq_last(expr* s);
expr_ref mk_seq_butlast(expr* s);
bool try_get_unit_values(expr* s, expr_ref_vector& result);
//replace b in a by c into result
void replace_all_subvectors(expr_ref_vector const& as, expr_ref_vector const& bs, expr* c, expr_ref_vector& result);
// Calculate derivative, memoized and enforcing a normal form // Calculate derivative, memoized and enforcing a normal form
expr_ref is_nullable_rec(expr* r); expr_ref is_nullable_rec(expr* r);
expr_ref mk_derivative_rec(expr* ele, expr* r); expr_ref mk_derivative_rec(expr* ele, expr* r);
@ -344,6 +354,16 @@ public:
return result; return result;
} }
/*
* makes concat and simplifies
*/
expr_ref mk_re_append(expr* r1, expr* r2) {
expr_ref result(m());
if (mk_re_concat(r1, r2, result) == BR_FAILED)
result = re().mk_concat(r1, r2);
return result;
}
/** /**
* check if regular expression is of the form all ++ s ++ all ++ t + u ++ all, where, s, t, u are sequences * check if regular expression is of the form all ++ s ++ all ++ t + u ++ all, where, s, t, u are sequences
*/ */

View file

@ -911,7 +911,7 @@ unsigned seq_util::str::max_length(expr* s) const {
return UINT_MAX; return UINT_MAX;
}; };
while (is_concat(s, s1, s2)) { while (is_concat(s, s1, s2)) {
result = u.max_plus(get_length(s), result); result = u.max_plus(get_length(s1), result);
s = s2; s = s2;
} }
result = u.max_plus(get_length(s), result); result = u.max_plus(get_length(s), result);
@ -1058,6 +1058,7 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) {
*/ */
std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const { std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const {
SASSERT(re.u.is_seq(s)); SASSERT(re.u.is_seq(s));
zstring z;
if (re.u.str.is_empty(s)) if (re.u.str.is_empty(s))
out << "()"; out << "()";
else if (re.u.str.is_unit(s)) else if (re.u.str.is_unit(s))
@ -1068,6 +1069,10 @@ std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s)
for (expr* e : es) for (expr* e : es)
compact_helper_seq(out, e); compact_helper_seq(out, e);
} }
else if (re.u.str.is_string(s, z)) {
for (unsigned i = 0; i < z.length(); i++)
out << (char)z[i];
}
//using braces to indicate 'full' output //using braces to indicate 'full' output
//for example an uninterpreted constant X will be printed as {X} //for example an uninterpreted constant X will be printed as {X}
//while a unit sequence "X" will be printed as X //while a unit sequence "X" will be printed as X
@ -1100,7 +1105,7 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const {
std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const { std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
expr* e; expr* e;
unsigned n = 0; unsigned n = 0;
if (re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) { if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) {
char c = (char)n; char c = (char)n;
if (c == '\n') if (c == '\n')
out << "\\n"; out << "\\n";
@ -1148,7 +1153,11 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
std::ostream& seq_util::rex::pp::display(std::ostream& out) const { std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr; expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr;
unsigned lo = 0, hi = 0; unsigned lo = 0, hi = 0;
if (re.is_full_char(e)) if (re.u.is_char(e))
return seq_unit(out, e);
else if (re.u.is_seq(e))
return compact_helper_seq(out, e);
else if (re.is_full_char(e))
return out << "."; return out << ".";
else if (re.is_full_seq(e)) else if (re.is_full_seq(e))
return out << ".*"; return out << ".*";
@ -1163,9 +1172,9 @@ std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
else if (re.is_concat(e, r1, r2)) else if (re.is_concat(e, r1, r2))
return out << pp(re, r1) << pp(re, r2); return out << pp(re, r1) << pp(re, r2);
else if (re.is_union(e, r1, r2)) else if (re.is_union(e, r1, r2))
return out << pp(re, r1) << "|" << pp(re, r2); return out << "(" << pp(re, r1) << "|" << pp(re, r2) << ")";
else if (re.is_intersection(e, r1, r2)) else if (re.is_intersection(e, r1, r2))
return out << "(" << pp(re, r1) << (html_encode ? ")&amp;(": ")&(" ) << pp(re, r2) << ")"; return out << "(" << pp(re, r1) << "&amp;" /*(html_encode ? ")&amp;(" : ")&(")*/ << pp(re, r2) << ")";
else if (re.is_complement(e, r1)) { else if (re.is_complement(e, r1)) {
if (can_skip_parenth(r1)) if (can_skip_parenth(r1))
return out << "~" << pp(re, r1); return out << "~" << pp(re, r1);

View file

@ -2022,10 +2022,14 @@ namespace sat {
} }
remove_bin_clauses(pos_l); remove_bin_clauses(pos_l);
remove_bin_clauses(neg_l); remove_bin_clauses(neg_l);
{
clause_use_list& pos_occs = m_use_list.get(pos_l);
clause_use_list& neg_occs = m_use_list.get(neg_l);
remove_clauses(pos_occs, pos_l); remove_clauses(pos_occs, pos_l);
remove_clauses(neg_occs, neg_l); remove_clauses(neg_occs, neg_l);
pos_occs.reset(); pos_occs.reset();
neg_occs.reset(); neg_occs.reset();
}
return true; return true;
} }

View file

@ -418,6 +418,7 @@ namespace arith {
expr* p = nullptr, * q = nullptr; expr* p = nullptr, * q = nullptr;
VERIFY(a.is_idiv(n, p, q)); VERIFY(a.is_idiv(n, p, q));
theory_var v1 = internalize_def(p); theory_var v1 = internalize_def(p);
ensure_column(v1);
lp::impq r1 = get_ivalue(v1); lp::impq r1 = get_ivalue(v1);
rational r2; rational r2;

View file

@ -73,7 +73,7 @@ namespace array {
obj_map<expr, unsigned> num_occ; obj_map<expr, unsigned> num_occ;
for (euf::enode* p : euf::enode_parents(n->get_root())) { for (euf::enode* p : euf::enode_parents(n->get_root())) {
if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n->get_root()) { if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n->get_root()) {
expr* v = values.get(p->get_root_id()); expr* v = values.get(p->get_root_id(), nullptr);
if (!v) if (!v)
continue; continue;
unsigned no = 0; unsigned no = 0;
@ -92,7 +92,7 @@ namespace array {
for (euf::enode* p : euf::enode_parents(n)) { for (euf::enode* p : euf::enode_parents(n)) {
if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n) { if (a.is_select(p->get_expr()) && p->get_arg(0)->get_root() == n) {
expr* value = values.get(p->get_root_id()); expr* value = values.get(p->get_root_id(), nullptr);
if (!value || value == fi->get_else()) if (!value || value == fi->get_else())
continue; continue;
args.reset(); args.reset();

View file

@ -490,6 +490,10 @@ namespace q {
TRACE("q", tout << "add " << mk_pp(_q, m) << "\n";); TRACE("q", tout << "add " << mk_pp(_q, m) << "\n";);
clause* c = clausify(_q); clause* c = clausify(_q);
quantifier* q = c->q(); quantifier* q = c->q();
if (m_q2clauses.contains(q)) {
dealloc(c);
return;
}
ensure_ground_enodes(*c); ensure_ground_enodes(*c);
m_clauses.push_back(c); m_clauses.push_back(c);
m_q2clauses.insert(q, c->index()); m_q2clauses.insert(q, c->index());

View file

@ -414,7 +414,7 @@ namespace q {
enode_vector m_candidates; enode_vector m_candidates;
#ifdef Z3DEBUG #ifdef Z3DEBUG
egraph * m_egraph; egraph * m_egraph;
ptr_vector<app> m_patterns; svector<std::pair<quantifier*, app*>> m_patterns;
#endif #endif
#ifdef _PROFILE_MAM #ifdef _PROFILE_MAM
stopwatch m_watch; stopwatch m_watch;
@ -568,7 +568,7 @@ namespace q {
m_egraph = ctx; m_egraph = ctx;
} }
ptr_vector<app> & get_patterns() { svector<std::pair<quantifier*, app*>> & get_patterns() {
return m_patterns; return m_patterns;
} }
#endif #endif
@ -578,8 +578,8 @@ namespace q {
if (m_egraph) { if (m_egraph) {
ast_manager & m = m_egraph->get_manager(); ast_manager & m = m_egraph->get_manager();
out << "patterns:\n"; out << "patterns:\n";
for (app* a : m_patterns) for (auto [q, a] : m_patterns)
out << mk_pp(a, m) << "\n"; out << mk_pp(q, m) << " " << mk_pp(a, m) << "\n";
} }
#endif #endif
out << "function: " << m_root_lbl->get_name(); out << "function: " << m_root_lbl->get_name();
@ -2862,12 +2862,11 @@ namespace q {
// The E-matching engine that was built when all + and * applications were binary. // The E-matching engine that was built when all + and * applications were binary.
// We ignore the pattern if it does not have the expected number of arguments. // We ignore the pattern if it does not have the expected number of arguments.
// This is not the ideal solution, but it avoids possible crashes. // This is not the ideal solution, but it avoids possible crashes.
if (tree->expected_num_args() == p->get_num_args()) { if (tree->expected_num_args() == p->get_num_args())
m_compiler.insert(tree, qa, mp, first_idx, false); m_compiler.insert(tree, qa, mp, first_idx, false);
} }
} DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(std::make_pair(qa, mp));
DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(mp); ctx.push(push_back_trail<std::pair<quantifier*,app*>, false>(m_trees[lbl_id]->get_patterns())););
ctx.push(push_back_trail<app*, false>(m_trees[lbl_id]->get_patterns())););
TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout);); TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout););
} }

View file

@ -44,6 +44,7 @@ class pb_preprocess_tactic : public tactic {
struct rec { unsigned_vector pos, neg; rec() { } }; struct rec { unsigned_vector pos, neg; rec() { } };
typedef obj_map<app, rec> var_map; typedef obj_map<app, rec> var_map;
ast_manager& m; ast_manager& m;
expr_ref_vector m_trail;
pb_util pb; pb_util pb;
var_map m_vars; var_map m_vars;
unsigned_vector m_ge; unsigned_vector m_ge;
@ -99,7 +100,7 @@ class pb_preprocess_tactic : public tactic {
public: public:
pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()): pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()):
m(m), pb(m), m_r(m) {} m(m), m_trail(m), pb(m), m_r(m) {}
~pb_preprocess_tactic() override {} ~pb_preprocess_tactic() override {}
@ -235,6 +236,7 @@ private:
m_ge.reset(); m_ge.reset();
m_other.reset(); m_other.reset();
m_vars.reset(); m_vars.reset();
m_trail.reset();
} }
expr* negate(expr* e) { expr* negate(expr* e) {
@ -410,6 +412,7 @@ private:
void insert(unsigned i, app* e, bool pos) { void insert(unsigned i, app* e, bool pos) {
SASSERT(is_uninterp_const(e)); SASSERT(is_uninterp_const(e));
if (!m_vars.contains(e)) { if (!m_vars.contains(e)) {
m_trail.push_back(e);
m_vars.insert(e, rec()); m_vars.insert(e, rec());
} }
if (pos) { if (pos) {
@ -504,10 +507,7 @@ private:
bool found = false; bool found = false;
for (unsigned j = 0; j < args2.size(); ++j) { for (unsigned j = 0; j < args2.size(); ++j) {
if (is_complement(args1.get(i), args2.get(j))) { if (is_complement(args1.get(i), args2.get(j))) {
if (i == 0) { if (i == 0 || min_coeff > coeffs2[j]) {
min_coeff = coeffs2[j];
}
else if (min_coeff > coeffs2[j]) {
min_coeff = coeffs2[j]; min_coeff = coeffs2[j];
min_index = j; min_index = j;
} }

View file

@ -93,6 +93,7 @@ public:
g->update(idx, f_new, new_pr, g->dep(idx)); g->update(idx, f_new, new_pr, g->dep(idx));
} }
if (mc) g->add(mc.get()); if (mc) g->add(mc.get());
TRACE("invertible_tactic", g->display(tout););
g->inc_depth(); g->inc_depth();
} }
result.push_back(g.get()); result.push_back(g.get());
@ -223,28 +224,46 @@ private:
rational r; rational r;
app* a = to_app(v); app* a = to_app(v);
expr* fst_arg = a->get_arg(0); expr* fst_arg = a->get_arg(0);
bool fst_is_var = is_var(fst_arg);
for (unsigned i = 0, n = a->get_num_args(); i != n; ++i) { for (expr* arg : *a)
auto arg = a->get_arg(i); if (!m_parents[arg->get_id()].get())
if (!m_parents[arg->get_id()].get() || is_var(arg) != fst_is_var)
return false; return false;
if (fst_is_var && to_var(arg)->get_idx() >= max_var) if (is_var(fst_arg)) {
for (expr* arg : *a) {
if (!is_var(arg))
return false; return false;
if (to_var(arg)->get_idx() >= max_var)
if (m_bv.is_numeral(arg, r) && r != mdl)
return false; return false;
}
if (i > 0 && !is_var(arg) && (!is_app(arg) || to_app(arg)->get_num_args() > 0)) }
else {
bool first = true;
for (expr* arg : *a) {
if (!is_app(arg))
return false; return false;
if (is_uninterp_const(arg))
continue;
if (m_bv.is_numeral(arg, r) && r == mdl) {
if (first || mdl.is_zero()) {
first = false;
continue;
}
else
return false;
}
return false;
}
} }
if (mc) { if (mc) {
ensure_mc(mc); ensure_mc(mc);
expr_ref num(m_bv.mk_numeral(mdl, fst_arg->get_sort()), m); expr_ref num(m_bv.mk_numeral(mdl, fst_arg->get_sort()), m);
for (unsigned i = 1, n = a->get_num_args(); i != n; ++i) { for (unsigned i = 1, n = a->get_num_args(); i != n; ++i) {
(*mc)->add(a->get_arg(i), num); expr* arg = a->get_arg(i);
if (m_bv.is_numeral(arg))
continue;
(*mc)->add(arg, num);
} }
} }
new_v = fst_arg; new_v = fst_arg;
@ -327,7 +346,6 @@ private:
// //
unsigned sz = m_bv.get_bv_size(p); unsigned sz = m_bv.get_bv_size(p);
expr_ref bit1(m_bv.mk_numeral(1, 1), m); expr_ref bit1(m_bv.mk_numeral(1, 1), m);
new_v = m_bv.mk_numeral(0, sz);
unsigned sh = 0; unsigned sh = 0;
@ -335,9 +353,10 @@ private:
r /= rational(2); r /= rational(2);
++sh; ++sh;
} }
if (r.is_pos() && sh > 0) { if (r.is_pos() && sh > 0)
new_v = m_bv.mk_concat(m_bv.mk_extract(sz-sh-1, 0, v), m_bv.mk_numeral(0, sh)); new_v = m_bv.mk_concat(m_bv.mk_extract(sz-sh-1, 0, v), m_bv.mk_numeral(0, sh));
} else
new_v = v;
if (mc && !r.is_zero()) { if (mc && !r.is_zero()) {
ensure_mc(mc); ensure_mc(mc);
expr_ref def(m); expr_ref def(m);
@ -414,6 +433,38 @@ private:
return true; return true;
} }
} }
//
// v <= u
// => u + 1 == 0 or delta
// v := delta ? u : u + 1
//
if (m_bv.is_bv_ule(p, e1, e2) && e1 == v && mc) {
ensure_mc(mc);
unsigned sz = m_bv.get_bv_size(e2);
expr_ref delta(m.mk_fresh_const("ule", m.mk_bool_sort()), m);
expr_ref succ_e2(m_bv.mk_bv_add(e2, m_bv.mk_numeral(1, sz)), m);
new_v = m.mk_or(delta, m.mk_eq(succ_e2, m_bv.mk_numeral(0, sz)));
(*mc)->hide(delta);
(*mc)->add(v, m.mk_ite(delta, e2, succ_e2));
return true;
}
//
// u <= v
// => u == 0 or delta
// v := delta ? u : u - 1
//
if (m_bv.is_bv_ule(p, e1, e2) && e2 == v && mc) {
ensure_mc(mc);
unsigned sz = m_bv.get_bv_size(e1);
expr_ref delta(m.mk_fresh_const("ule", m.mk_bool_sort()), m);
expr_ref pred_e1(m_bv.mk_bv_sub(e1, m_bv.mk_numeral(1, sz)), m);
new_v = m.mk_or(delta, m.mk_eq(e1, m_bv.mk_numeral(0, sz)));
(*mc)->hide(delta);
(*mc)->add(v, m.mk_ite(delta, e1, pred_e1));
return true;
}
return false; return false;
} }

View file

@ -41,31 +41,41 @@ class solver_subsumption_tactic : public tactic {
/** /**
* Check subsumption (a \/ b \/ c) * Check subsumption (a \/ b \/ c)
* if (a \/ b) is already implied *
* Use a naive algorithm (not binary disection here) * If
* F |= (a \/ ~b \/ c)
* Then replace (a \/ b \/ c) by (a \/ c)
*
* If
* F |= (a \/ b \/ c)
* Then replace (a \/ b \/ c) by true
*
*/ */
bool simplify(expr_ref& f) { bool simplify(expr_ref& f) {
expr_ref_vector fmls(m), ors(m), nors(m), prefix(m);
expr_ref nf(m.mk_not(f), m);
fmls.push_back(nf);
lbool is_sat = m_solver->check_sat(fmls);
if (is_sat == l_false) {
f = m.mk_true();
return true;
}
if (!m.is_or(f)) if (!m.is_or(f))
return false; return false;
expr_ref_vector ors(m);
ors.append(to_app(f)->get_num_args(), to_app(f)->get_args()); ors.append(to_app(f)->get_num_args(), to_app(f)->get_args());
expr_ref_vector prefix(m); for (expr* arg : ors)
nors.push_back(mk_not(m, arg));
for (unsigned i = 0; i < ors.size(); ++i) { for (unsigned i = 0; i < ors.size(); ++i) {
expr_ref_vector fmls(m); expr* arg = ors.get(i);
fmls.append(prefix); expr_ref save(nors.get(i), m);
for (unsigned k = i + 1; k < ors.size(); ++k) nors[i] = arg;
fmls.push_back(m.mk_not(ors.get(k))); is_sat = m_solver->check_sat(nors);
lbool is_sat = m_solver->check_sat(fmls); nors[i] = save;
if (is_sat == l_false) if (is_sat == l_false)
continue; nors[i] = m.mk_true();
fmls.reset(); else
fmls.push_back(ors.get(i)); prefix.push_back(arg);
is_sat = m_solver->check_sat(fmls);
if (is_sat == l_false)
continue;
prefix.push_back(ors.get(i));
} }
if (ors.size() != prefix.size()) { if (ors.size() != prefix.size()) {
ors.reset(); ors.reset();
@ -131,7 +141,7 @@ public:
} }
void collect_param_descrs(param_descrs& r) override { void collect_param_descrs(param_descrs& r) override {
r.insert("max_conflicts", CPK_UINT, "(default: 10) maximal number of conflicts allowed per solver call."); r.insert("max_conflicts", CPK_UINT, "(default: 2) maximal number of conflicts allowed per solver call.");
} }
void operator()(goal_ref const& g, goal_ref_buffer& result) override { void operator()(goal_ref const& g, goal_ref_buffer& result) override {

View file

@ -13,7 +13,7 @@ Abstract:
Author: Author:
Caleb Stanford (calebstanford-msr / cdstanford) 2020-7 Caleb Stanford (calebstanford-msr / cdstanford) 2020-7
Margus Veanes 2020-8
--*/ --*/
#include "state_graph.h" #include "state_graph.h"