3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-10-07 00:11:55 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2022-07-01 16:11:17 +02:00
commit e5e79c1d4b
398 changed files with 24548 additions and 4983 deletions

View file

@ -78,7 +78,7 @@ namespace {
public:
unsigned char operator()(func_decl * lbl) {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
if (lbl_id >= m_lbl2hash.size())
m_lbl2hash.resize(lbl_id + 1, -1);
if (m_lbl2hash[lbl_id] == -1) {
@ -1952,7 +1952,7 @@ namespace {
m_args[i] = m_registers[pc->m_iregs[i]]->get_root();
SASSERT(n != 0);
do {
if (n->get_decl() == f) {
if (n->get_decl() == f && n->get_num_args() == num_args) {
unsigned i = 0;
for (; i < num_args; i++) {
if (n->get_arg(i)->get_root() != m_args[i])
@ -2906,7 +2906,7 @@ namespace {
SASSERT(first_idx < mp->get_num_args());
app * p = to_app(mp->get_arg(first_idx));
func_decl * lbl = p->get_decl();
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
m_trees.reserve(lbl_id+1, nullptr);
if (m_trees[lbl_id] == nullptr) {
m_trees[lbl_id] = m_compiler.mk_tree(qa, mp, first_idx, false);
@ -2935,7 +2935,7 @@ namespace {
}
code_tree * get_code_tree_for(func_decl * lbl) const {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
if (lbl_id < m_trees.size())
return m_trees[lbl_id];
else
@ -3165,11 +3165,11 @@ namespace {
}
bool is_plbl(func_decl * lbl) const {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
return lbl_id < m_is_plbl.size() && m_is_plbl[lbl_id];
}
bool is_clbl(func_decl * lbl) const {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
return lbl_id < m_is_clbl.size() && m_is_clbl[lbl_id];
}
@ -3182,7 +3182,7 @@ namespace {
}
void update_clbls(func_decl * lbl) {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
m_is_clbl.reserve(lbl_id+1, false);
TRACE("trigger_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";);
TRACE("mam_bug", tout << "update_clbls: " << lbl->get_name() << " is already clbl: " << m_is_clbl[lbl_id] << "\n";);
@ -3222,7 +3222,7 @@ namespace {
}
void update_plbls(func_decl * lbl) {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
m_is_plbl.reserve(lbl_id+1, false);
TRACE("trigger_bug", tout << "update_plbls: " << lbl->get_name() << " is already plbl: " << m_is_plbl[lbl_id] << ", lbl_id: " << lbl_id << "\n";
tout << "mam: " << this << "\n";);
@ -3744,7 +3744,7 @@ namespace {
app * p = to_app(mp->get_arg(0));
func_decl * lbl = p->get_decl();
if (m_context.get_num_enodes_of(lbl) > 0) {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
m_tmp_trees.reserve(lbl_id+1, 0);
if (m_tmp_trees[lbl_id] == 0) {
m_tmp_trees[lbl_id] = m_compiler.mk_tree(qa, mp, 0, false);
@ -3757,7 +3757,7 @@ namespace {
}
for (func_decl * lbl : m_tmp_trees_to_delete) {
unsigned lbl_id = lbl->get_decl_id();
unsigned lbl_id = lbl->get_small_id();
code_tree * tmp_tree = m_tmp_trees[lbl_id];
SASSERT(tmp_tree != 0);
SASSERT(m_context.get_num_enodes_of(lbl) > 0);
@ -3963,7 +3963,7 @@ namespace {
unsigned h = m_lbl_hasher(lbl);
TRACE("trigger_bug", tout << "lbl: " << lbl->get_name() << " is_clbl(lbl): " << is_clbl(lbl)
<< ", is_plbl(lbl): " << is_plbl(lbl) << ", h: " << h << "\n";
tout << "lbl_id: " << lbl->get_decl_id() << "\n";);
tout << "lbl_id: " << lbl->get_small_id() << "\n";);
if (is_clbl(lbl))
update_lbls(n, h);
if (is_plbl(lbl))

View file

@ -232,6 +232,7 @@ namespace smt {
expr_ref s_instance(m);
proof_ref pr(m);
m_context.get_rewriter()(instance, s_instance, pr);
TRACE("qi_queue_bug", tout << "new instance after simplification:\n" << s_instance << "\n";);
if (m.is_true(s_instance)) {
TRACE("checker", tout << "reduced to true, before:\n" << mk_ll_pp(instance, m););

View file

@ -42,6 +42,7 @@ Revision History:
#include "smt/smt_model_finder.h"
#include "smt/smt_parallel.h"
#include "smt/smt_arith_value.h"
#include <iostream>
namespace smt {
@ -1847,24 +1848,30 @@ namespace smt {
}
}
bool_var var;
lbool phase = l_undef;
m_case_split_queue->next_case_split(var, phase);
bool is_pos;
bool used_queue = false;
if (!has_split_candidate(var, is_pos)) {
lbool phase = l_undef;
m_case_split_queue->next_case_split(var, phase);
used_queue = true;
if (var == null_bool_var)
return false;
if (var == null_bool_var) {
return false;
TRACE_CODE({
static unsigned counter = 0;
counter++;
if (counter % 100 == 0) {
TRACE("activity_profile",
for (unsigned i=0; i<get_num_bool_vars(); i++) {
tout << get_activity(i) << " ";
}
tout << "\n";);
}});
is_pos = guess(var, phase);
}
TRACE_CODE({
static unsigned counter = 0;
counter++;
if (counter % 100 == 0) {
TRACE("activity_profile",
for (unsigned i=0; i<get_num_bool_vars(); i++) {
tout << get_activity(i) << " ";
}
tout << "\n";);
}});
m_stats.m_num_decisions++;
push_scope();
@ -1872,13 +1879,13 @@ namespace smt {
TRACE("decide_detail", tout << mk_pp(bool_var2expr(var), m) << "\n";);
bool is_pos = guess(var, phase);
literal l(var, false);
bool_var original_choice = var;
if (decide_user_interference(var, is_pos)) {
m_case_split_queue->unassign_var_eh(original_choice);
if (used_queue)
m_case_split_queue->unassign_var_eh(original_choice);
l = literal(var, false);
}
@ -2904,8 +2911,14 @@ namespace smt {
return m_user_propagator && m_user_propagator->has_fixed() && n->get_th_var(m_user_propagator->get_family_id()) != null_theory_var;
}
bool context::has_split_candidate(bool_var& var, bool& is_pos) {
if (!m_user_propagator)
return false;
return m_user_propagator->get_case_split(var, is_pos);
}
bool context::decide_user_interference(bool_var& var, bool& is_pos) {
if (!m_user_propagator || !m_user_propagator->has_decide())
if (!m_user_propagator)
return false;
bool_var old = var;
m_user_propagator->decide(var, is_pos);
@ -3168,7 +3181,7 @@ namespace smt {
}
else {
expr_ref proxy(m), fml(m);
proxy = m.mk_fresh_const("proxy", m.mk_bool_sort());
proxy = m.mk_fresh_const(symbol(), m.mk_bool_sort());
fml = m.mk_implies(proxy, e);
m_asserted_formulas.assert_expr(fml);
asm2proxy.push_back(std::make_pair(e, proxy));
@ -3737,15 +3750,12 @@ namespace smt {
reset_model();
if (m_last_search_failure != OK) {
if (m_last_search_failure != OK)
return false;
}
if (status == l_false) {
if (status == l_false)
return false;
}
if (status == l_true && !m_qmanager->has_quantifiers() && !m_has_lambda) {
if (status == l_true && !m_qmanager->has_quantifiers() && !has_lambda())
return false;
}
if (status == l_true && m_qmanager->has_quantifiers()) {
// possible outcomes DONE l_true, DONE l_undef, CONTINUE
mk_proto_model();
@ -3766,7 +3776,7 @@ namespace smt {
break;
}
}
if (status == l_true && m_has_lambda) {
if (status == l_true && has_lambda()) {
m_last_search_failure = LAMBDAS;
status = l_undef;
return false;
@ -3946,8 +3956,7 @@ namespace smt {
if (m_fparams.m_model_on_final_check) {
mk_proto_model();
model_pp(std::cout, *m_proto_model);
std::cout << "END_OF_MODEL\n";
std::cout.flush();
std::cout << "END_OF_MODEL" << std::endl;
}
m_stats.m_num_final_checks++;
@ -4010,7 +4019,7 @@ namespace smt {
TRACE("final_check_step", tout << "RESULT final_check: " << result << "\n";);
if (result == FC_GIVEUP && f != OK)
m_last_search_failure = f;
if (result == FC_DONE && m_has_lambda) {
if (result == FC_DONE && has_lambda()) {
m_last_search_failure = LAMBDAS;
result = FC_GIVEUP;
}
@ -4468,9 +4477,8 @@ namespace smt {
return false;
}
case 1: {
if (m_qmanager->is_shared(n)) {
if (m_qmanager->is_shared(n) && !m.is_lambda_def(n->get_expr()) && !m_lambdas.contains(n))
return true;
}
// the variable is shared if the equivalence class of n
// contains a parent application.
@ -4482,6 +4490,8 @@ namespace smt {
app* p = parent->get_expr();
family_id fid = p->get_family_id();
if (fid != th_id && fid != m.get_basic_family_id()) {
if (is_beta_redex(parent, n))
continue;
TRACE("is_shared", tout << enode_pp(n, *this)
<< "\nis shared because of:\n"
<< enode_pp(parent, *this) << "\n";);
@ -4522,6 +4532,12 @@ namespace smt {
}
}
bool context::is_beta_redex(enode* p, enode* n) const {
family_id th_id = p->get_expr()->get_family_id();
theory * th = get_theory(th_id);
return th && th->is_beta_redex(p, n);
}
bool context::get_value(enode * n, expr_ref & value) {
sort * s = n->get_sort();
family_id fid = s->get_family_id();

View file

@ -531,22 +531,22 @@ namespace smt {
}
unsigned get_num_enodes_of(func_decl const * decl) const {
unsigned id = decl->get_decl_id();
unsigned id = decl->get_small_id();
return id < m_decl2enodes.size() ? m_decl2enodes[id].size() : 0;
}
enode_vector const& enodes_of(func_decl const * d) const {
unsigned id = d->get_decl_id();
unsigned id = d->get_small_id();
return id < m_decl2enodes.size() ? m_decl2enodes[id] : m_empty_vector;
}
enode_vector::const_iterator begin_enodes_of(func_decl const * decl) const {
unsigned id = decl->get_decl_id();
unsigned id = decl->get_small_id();
return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : nullptr;
}
enode_vector::const_iterator end_enodes_of(func_decl const * decl) const {
unsigned id = decl->get_decl_id();
unsigned id = decl->get_small_id();
return id < m_decl2enodes.size() ? m_decl2enodes[id].end() : nullptr;
}
@ -773,7 +773,12 @@ namespace smt {
void internalize_quantifier(quantifier * q, bool gate_ctx);
bool m_has_lambda = false;
obj_map<enode, quantifier*> m_lambdas;
bool has_lambda();
bool is_beta_redex(enode* p, enode* n) const;
void internalize_lambda(quantifier * q);
void internalize_formula_core(app * n, bool gate_ctx);
@ -783,6 +788,7 @@ namespace smt {
friend class set_enode_flag_trail;
public:
void set_enode_flag(bool_var v, bool is_new_var);
protected:
@ -1748,6 +1754,8 @@ namespace smt {
bool watches_fixed(enode* n) const;
bool has_split_candidate(bool_var& var, bool& is_pos);
bool decide_user_interference(bool_var& var, bool& is_pos);
void assign_fixed(enode* n, expr* val, unsigned sz, literal const* explain);

View file

@ -415,6 +415,7 @@ namespace smt {
st.update("final checks", m_stats.m_num_final_checks);
st.update("added eqs", m_stats.m_num_add_eq);
st.update("mk clause", m_stats.m_num_mk_clause);
st.update("mk clause binary", m_stats.m_num_mk_bin_clause);
st.update("del clause", m_stats.m_num_del_clause);
st.update("dyn ack", m_stats.m_num_dyn_ack);
st.update("interface eqs", m_stats.m_num_interface_eqs);

View file

@ -136,9 +136,9 @@ namespace smt {
}
void context::display_profile_res_sub(std::ostream & out) const {
display_var_occs_histogram(std::cerr);
display_num_min_occs(std::cerr);
std::cerr << "\n";
display_var_occs_histogram(out);
display_num_min_occs(out);
out << "\n";
}
void context::display_profile(std::ostream & out) const {

View file

@ -78,6 +78,8 @@ namespace smt {
unsigned m_merge_tf:1; //!< True if the enode should be merged with true/false when the associated boolean variable is assigned.
unsigned m_cgc_enabled:1; //!< True if congruence closure is enabled for this enode.
unsigned m_iscope_lvl; //!< When the enode was internalized
bool m_proof_is_logged; //!< Indicates that the proof for the enode being equal to its root is in the log.
signed char m_lbl_hash; //!< It is different from -1, if enode is used in a pattern
/*
The following property is valid for m_parents
@ -96,8 +98,6 @@ namespace smt {
enode_vector m_parents; //!< Parent enodes of the equivalence class.
id_var_list<> m_th_var_list; //!< List of theories that 'care' about this enode.
trans_justification m_trans; //!< A justification for the enode being equal to its root.
bool m_proof_is_logged; //!< Indicates that the proof for the enode being equal to its root is in the log.
signed char m_lbl_hash; //!< It is different from -1, if enode is used in a pattern
approx_set m_lbls;
approx_set m_plbls;
enode * m_args[0]; //!< Cached args
@ -171,7 +171,7 @@ namespace smt {
unsigned get_expr_id() const { return m_owner->get_id(); }
func_decl * get_decl() const { return m_owner->get_decl(); }
unsigned get_decl_id() const { return m_owner->get_decl()->get_decl_id(); }
unsigned get_decl_id() const { return m_owner->get_decl()->get_small_id(); }
sort* get_sort() const { return m_owner->get_sort(); }

View file

@ -350,6 +350,8 @@ namespace smt {
- gate_ctx is true if the expression is in the context of a logical gate.
*/
void context::internalize(expr * n, bool gate_ctx) {
if (memory::above_high_watermark())
throw default_exception("resource limit exceeded during internalization");
internalize_deep(n);
internalize_rec(n, gate_ctx);
}
@ -576,20 +578,19 @@ namespace smt {
m_qmanager->add(q, generation);
}
void context::internalize_lambda(quantifier * q) {
TRACE("internalize_quantifier", tout << mk_pp(q, m) << "\n";);
SASSERT(is_lambda(q));
if (e_internalized(q)) {
if (e_internalized(q))
return;
}
app_ref lam_name(m.mk_fresh_const("lambda", q->get_sort()), m);
app_ref eq(m), lam_app(m);
expr_ref_vector vars(m);
vars.push_back(lam_name);
unsigned sz = q->get_num_decls();
for (unsigned i = 0; i < sz; ++i) {
for (unsigned i = 0; i < sz; ++i)
vars.push_back(m.mk_var(sz - i - 1, q->get_decl_sort(i)));
}
array_util autil(m);
lam_app = autil.mk_select(vars.size(), vars.data());
eq = m.mk_eq(lam_app, q->get_expr());
@ -597,15 +598,28 @@ namespace smt {
expr * patterns[1] = { m.mk_pattern(lam_app) };
fa = m.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m.lambda_def_qid(), symbol::null, 1, patterns);
internalize_quantifier(fa, true);
if (!e_internalized(lam_name)) internalize_uninterpreted(lam_name);
m_app2enode.setx(q->get_id(), get_enode(lam_name), nullptr);
if (!e_internalized(lam_name))
internalize_uninterpreted(lam_name);
enode* lam_node = get_enode(lam_name);
push_trail(insert_obj_map<enode, quantifier*>(m_lambdas, lam_node));
m_lambdas.insert(lam_node, q);
m_app2enode.setx(q->get_id(), lam_node, nullptr);
m_l_internalized_stack.push_back(q);
m_trail_stack.push_back(&m_mk_lambda_trail);
bool_var bv = get_bool_var(fa);
assign(literal(bv, false), nullptr);
mark_as_relevant(bv);
push_trail(value_trail<bool>(m_has_lambda));
m_has_lambda = true;
}
bool context::has_lambda() {
for (auto const & [n, q] : m_lambdas) {
if (n->get_class_size() != 1)
return true;
for (enode* p : enode::parents(n))
if (!is_beta_redex(p, n))
return true;
}
return false;
}
/**
@ -1008,7 +1022,7 @@ namespace smt {
}
}
if (!e->is_eq()) {
unsigned decl_id = n->get_decl()->get_decl_id();
unsigned decl_id = n->get_decl()->get_small_id();
if (decl_id >= m_decl2enodes.size())
m_decl2enodes.resize(decl_id+1);
m_decl2enodes[decl_id].push_back(e);
@ -1052,7 +1066,7 @@ namespace smt {
m_cg_table.erase(e);
}
if (e->get_num_args() > 0 && !e->is_eq()) {
unsigned decl_id = to_app(n)->get_decl()->get_decl_id();
unsigned decl_id = to_app(n)->get_decl()->get_small_id();
SASSERT(decl_id < m_decl2enodes.size());
SASSERT(m_decl2enodes[decl_id].back() == e);
m_decl2enodes[decl_id].pop_back();

View file

@ -18,8 +18,6 @@ Revision History:
--*/
#pragma once
#include<iostream>
namespace smt {
struct statistics {

View file

@ -321,6 +321,13 @@ namespace smt {
virtual bool is_shared(theory_var v) const {
return false;
}
/**
\brief Determine if node \c n under parent \c p is in a beta redex position.
*/
virtual bool is_beta_redex(enode* p, enode* n) const {
return false;
}
/**
\brief Return true if the theory has something to propagate

View file

@ -974,7 +974,7 @@ namespace smt {
/**
\brief A monomial is 'pure' if does not have a numeric coefficient.
*/
bool is_pure_monomial(expr * m) const { return m_util.is_mul(m) && (to_app(m)->get_num_args() > 2 || !m_util.is_numeral(to_app(m)->get_arg(0))); }
bool is_pure_monomial(expr * m) const;
bool is_pure_monomial(theory_var v) const { return is_pure_monomial(get_enode(v)->get_expr()); }
void mark_var(theory_var v, svector<theory_var> & vars, var_set & already_found);
void mark_dependents(theory_var v, svector<theory_var> & vars, var_set & already_found, row_set & already_visited_rows);

View file

@ -574,7 +574,8 @@ namespace smt {
lower = m_util.mk_ge(mod, zero);
upper = m_util.mk_le(mod, abs_divisor);
TRACE("div_axiom_bug",
tout << "eqz: " << eqz << " neq: " << eq << "\n";
tout << "eqz: " << eqz << "\n";
tout << "neq: " << eq << "\n";
tout << "lower: " << lower << "\n";
tout << "upper: " << upper << "\n";);
@ -583,14 +584,18 @@ namespace smt {
mk_axiom(eqz, upper, !m_util.is_numeral(abs_divisor));
rational k;
if (!m_util.is_numeral(divisor)) {
// (=> (> y 0) (<= (* y (div x y)) x))
// (=> (< y 0) ???)
expr_ref div_ge(m), div_non_pos(m);
if (m_util.is_zero(dividend)) {
mk_axiom(eqz, m.mk_eq(div, zero));
mk_axiom(eqz, m.mk_eq(mod, zero));
}
// (or (= y 0) (<= (* y (div x y)) x))
else if (!m_util.is_numeral(divisor)) {
expr_ref div_ge(m), div_le(m), ge(m), le(m);
div_ge = m_util.mk_ge(m_util.mk_sub(dividend, m_util.mk_mul(divisor, div)), zero);
s(div_ge);
div_non_pos = m_util.mk_le(divisor, zero);
mk_axiom(div_non_pos, div_ge, false);
s(div_ge);
mk_axiom(eqz, div_ge, false);
TRACE("arith", tout << eqz << " " << div_ge << "\n");
}
if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) &&
@ -3097,6 +3102,8 @@ namespace smt {
m_stats.m_conflicts++;
m_num_conflicts++;
TRACE("arith_conflict",
if (proof_rule)
tout << proof_rule << "\n";
tout << "scope: " << ctx.get_scope_level() << "\n";
for (unsigned i = 0; i < num_literals; i++) {
ctx.display_detailed_literal(tout, lits[i]);

View file

@ -624,11 +624,9 @@ template<typename Ext>
bool theory_arith<Ext>::check_monomial_assignments() {
bool computed_epsilon = false;
for (theory_var v : m_nl_monomials) {
TRACE("non_linear", tout << "v" << v << " is relevant: " << ctx.is_relevant(get_enode(v)) << "\n";
tout << "check_monomial_assignments result: " << check_monomial_assignment(v, computed_epsilon) << "\n";
tout << "computed_epsilon: " << computed_epsilon << "\n";);
TRACE("non_linear", tout << "v" << v << " is relevant: " << ctx.is_relevant(get_enode(v)) << "\n");
if (ctx.is_relevant(get_enode(v)) && !check_monomial_assignment(v, computed_epsilon)) {
TRACE("non_linear_failed", tout << "check_monomial_assignment failed for:\n" << mk_ismt2_pp(var2expr(v), get_manager()) << "\n";
TRACE("non_linear", tout << "check_monomial_assignment failed for:\n" << mk_ismt2_pp(var2expr(v), get_manager()) << "\n";
display_var(tout, v););
return false;
}
@ -1254,6 +1252,17 @@ bool theory_arith<Ext>::in_monovariate_monomials(buffer<coeff_expr> & p, expr *
}
template<typename Ext>
bool theory_arith<Ext>::is_pure_monomial(expr* mon) const {
if (!m_util.is_mul(mon))
return false;
app* p = to_app(mon);
for (expr* arg : *p)
if (m_util.is_numeral(arg) || m_util.is_mul(arg))
return false;
return true;
}
/**
\brief Display a nested form expression
*/
@ -2145,13 +2154,14 @@ void theory_arith<Ext>::set_gb_exhausted() {
// Scan the grobner basis eqs, and look for inconsistencies.
template<typename Ext>
bool theory_arith<Ext>::get_gb_eqs_and_look_for_conflict(ptr_vector<grobner::equation>& eqs, grobner& gb) {
TRACE("grobner", );
eqs.reset();
gb.get_equations(eqs);
TRACE("grobner_bug", tout << "after gb\n";);
TRACE("grobner", tout << "after gb\n";
for (grobner::equation* eq : eqs)
gb.display_equation(tout, *eq);
);
for (grobner::equation* eq : eqs) {
TRACE("grobner_bug", gb.display_equation(tout, *eq););
if (is_inconsistent(eq, gb) || is_inconsistent2(eq, gb)) {
TRACE("grobner", tout << "inconsistent: "; gb.display_equation(tout, *eq););
return true;

View file

@ -239,7 +239,6 @@ namespace smt {
//
bool theory_array::internalize_term_core(app * n) {
TRACE("array_bug", tout << mk_bounded_pp(n, m) << "\n";);
unsigned num_args = n->get_num_args();
for (expr* arg : *n)
ctx.internalize(arg, false);
// force merge-tf by re-internalizing expression.

View file

@ -272,12 +272,14 @@ namespace smt {
std::cout << smt << "\n";
std::cout << tns << "\n";
#endif
#if 0
if (tns == sz1) {
std::cout << "SEEN " << tms << "\n";
}
if (tns == sz2) {
std::cout << "SEEN " << smt << "\n";
}
#endif
ctx().push_trail(value_trail<bool>(i1.m_is_leaf, false));
ctx().push_trail(value_trail<bool>(i2.m_is_leaf, false));
expr_ref k1(m), k2(m), k3(m);

View file

@ -473,39 +473,21 @@ namespace smt {
return false;
}
#if 0
void theory_array_base::collect_shared_vars(sbuffer<theory_var> & result) {
TRACE("array_shared", tout << "collecting shared vars...\n";);
ptr_buffer<enode> to_unmark;
unsigned num_vars = get_num_vars();
for (unsigned i = 0; i < num_vars; i++) {
enode * n = get_enode(i);
if (ctx.is_relevant(n) && ctx.is_shared(n)) {
enode * r = n->get_root();
if (!r->is_marked() && is_array_sort(r)) {
TRACE("array_shared", tout << "new shared var: #" << r->get_expr_id() << "\n";);
r->set_mark();
to_unmark.push_back(r);
theory_var r_th_var = r->get_var(get_id());
SASSERT(r_th_var != null_theory_var);
result.push_back(r_th_var);
}
}
}
unmark_enodes(to_unmark.size(), to_unmark.c_ptr());
bool theory_array_base::is_beta_redex(enode* p, enode* n) const {
if (is_select(p))
return p->get_arg(0)->get_root() == n->get_root();
if (is_map(p))
return true;
return false;
}
#else
bool theory_array_base::is_select_arg(enode* r) {
for (enode* n : r->get_parents()) {
if (is_select(n)) {
for (unsigned i = 1; i < n->get_num_args(); ++i) {
if (r == n->get_arg(i)->get_root()) {
for (enode* n : r->get_parents())
if (is_select(n))
for (unsigned i = 1; i < n->get_num_args(); ++i)
if (r == n->get_arg(i)->get_root())
return true;
}
}
}
}
return false;
}
@ -536,7 +518,6 @@ namespace smt {
TRACE("array", tout << "collecting shared vars...\n" << unsigned_vector(result.size(), (unsigned*)result.data()) << "\n";);
unmark_enodes(to_unmark.size(), to_unmark.data());
}
#endif
/**
\brief Create interface variables for shared array variables.

View file

@ -142,6 +142,7 @@ namespace smt {
//
// --------------------------------------------------
bool is_shared(theory_var v) const override;
bool is_beta_redex(enode* p, enode* n) const override;
void collect_shared_vars(sbuffer<theory_var> & result);
unsigned mk_interface_eqs();

View file

@ -1842,11 +1842,11 @@ namespace smt {
unsigned sz = bits.size();
for (unsigned i = start_bit; i < sz; ++i) {
if (ctx.get_assignment(bits[i].var()) != l_undef)
if (ctx.get_assignment(bits[i].var()) == l_undef)
return bits[i].var();
}
for (unsigned i = 0; i < start_bit; ++i) {
if (ctx.get_assignment(bits[i].var()) != l_undef)
if (ctx.get_assignment(bits[i].var()) == l_undef)
return bits[i].var();
}

View file

@ -275,7 +275,7 @@ namespace smt {
else if (is_update_field(n)) {
assert_update_field_axioms(n);
}
else {
else if (m_util.is_datatype(n->get_sort())) {
sort * s = n->get_sort();
if (m_util.get_datatype_num_constructors(s) == 1) {
func_decl * c = m_util.get_datatype_constructors(s)->get(0);
@ -343,7 +343,7 @@ namespace smt {
}
arg = ctx.get_enode(def);
}
if (!m_util.is_datatype(s))
if (!m_util.is_datatype(s) && !m_sutil.is_seq(s))
continue;
if (is_attached_to_var(arg))
continue;
@ -393,7 +393,7 @@ namespace smt {
if (!is_attached_to_var(n) &&
(ctx.has_quantifiers() ||
(m_util.is_datatype(s) && m_util.has_nested_arrays()) ||
(m_util.is_datatype(s) && m_util.has_nested_rec()) ||
(m_util.is_datatype(s) && !s->is_infinite()))) {
mk_var(n);
}
@ -485,7 +485,10 @@ namespace smt {
for (int v = 0; v < num_vars; v++) {
if (v == static_cast<int>(m_find.find(v))) {
enode * node = get_enode(v);
if (m_util.is_recursive(node->get_sort()) && !oc_cycle_free(node) && occurs_check(node)) {
sort* s = node->get_sort();
if (!m_util.is_datatype(s))
continue;
if (m_util.is_recursive(s) && !oc_cycle_free(node) && occurs_check(node)) {
// conflict was detected...
// return...
return FC_CONTINUE;
@ -541,6 +544,17 @@ namespace smt {
}
}
}
sort* se = nullptr;
if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) {
for (enode* aarg : get_seq_args(arg)) {
if (aarg->get_root() == child->get_root()) {
if (aarg != child) {
m_used_eqs.push_back(enode_pair(aarg, child));
}
found = true;
}
}
}
}
VERIFY(found);
}
@ -587,6 +601,20 @@ namespace smt {
}
enode * parent = d->m_constructor;
oc_mark_on_stack(parent);
auto process_arg = [&](enode* aarg) {
if (oc_cycle_free(aarg))
return false;
if (oc_on_stack(aarg)) {
occurs_check_explain(parent, aarg);
return true;
}
if (m_util.is_datatype(aarg->get_sort())) {
m_parent.insert(aarg->get_root(), parent);
oc_push_stack(aarg);
}
return false;
};
for (enode * arg : enode::args(parent)) {
if (oc_cycle_free(arg)) {
continue;
@ -598,39 +626,61 @@ namespace smt {
}
// explore `arg` (with parent)
expr* earg = arg->get_expr();
sort* s = earg->get_sort();
sort* s = earg->get_sort(), *se = nullptr;
if (m_util.is_datatype(s)) {
m_parent.insert(arg->get_root(), parent);
oc_push_stack(arg);
}
else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) {
for (enode* aarg : get_array_args(arg)) {
if (oc_cycle_free(aarg)) {
continue;
}
if (oc_on_stack(aarg)) {
occurs_check_explain(parent, aarg);
else if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) {
for (enode* sarg : get_seq_args(arg))
if (process_arg(sarg))
return true;
}
if (m_util.is_datatype(aarg->get_sort())) {
m_parent.insert(aarg->get_root(), parent);
oc_push_stack(aarg);
}
}
}
}
else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) {
for (enode* aarg : get_array_args(arg))
if (process_arg(aarg))
return true;
}
}
return false;
}
ptr_vector<enode> const& theory_datatype::get_array_args(enode* n) {
m_array_args.reset();
theory_array* th = dynamic_cast<theory_array*>(ctx.get_theory(m_autil.get_family_id()));
for (enode* p : th->parent_selects(n)) {
m_array_args.push_back(p);
ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n) {
m_args.reset();
m_todo.reset();
auto add_todo = [&](enode* n) {
if (!n->is_marked()) {
n->set_mark();
m_todo.push_back(n);
}
};
for (enode* sib : *n)
add_todo(sib);
for (unsigned i = 0; i < m_todo.size(); ++i) {
enode* n = m_todo[i];
expr* e = n->get_expr();
if (m_sutil.str.is_unit(e))
m_args.push_back(n->get_arg(0));
else if (m_sutil.str.is_concat(e))
for (expr* arg : *to_app(e))
add_todo(ctx.get_enode(arg));
}
for (enode* n : m_todo)
n->unset_mark();
return m_args;
}
ptr_vector<enode> const& theory_datatype::get_array_args(enode* n) {
m_args.reset();
theory_array* th = dynamic_cast<theory_array*>(ctx.get_theory(m_autil.get_family_id()));
for (enode* p : th->parent_selects(n))
m_args.push_back(p);
app_ref def(m_autil.mk_default(n->get_expr()), m);
m_array_args.push_back(ctx.get_enode(def));
return m_array_args;
m_args.push_back(ctx.get_enode(def));
return m_args;
}
/**
@ -653,18 +703,19 @@ namespace smt {
enode * app = m_stack.back().second;
m_stack.pop_back();
if (oc_cycle_free(app)) continue;
if (oc_cycle_free(app))
continue;
TRACE("datatype", tout << "occurs check loop: " << enode_pp(app, ctx) << (op==ENTER?" enter":" exit")<< "\n";);
switch (op) {
case ENTER:
res = occurs_check_enter(app);
break;
res = occurs_check_enter(app);
break;
case EXIT:
oc_mark_cycle_free(app);
break;
oc_mark_cycle_free(app);
break;
}
}
@ -702,6 +753,7 @@ namespace smt {
theory(ctx, ctx.get_manager().mk_family_id("datatype")),
m_util(m),
m_autil(m),
m_sutil(m),
m_find(*this),
m_trail_stack() {
}

View file

@ -20,6 +20,7 @@ Revision History:
#include "util/union_find.h"
#include "ast/array_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "ast/datatype_decl_plugin.h"
#include "model/datatype_factory.h"
#include "smt/smt_theory.h"
@ -46,6 +47,7 @@ namespace smt {
datatype_util m_util;
array_util m_autil;
seq_util m_sutil;
ptr_vector<var_data> m_var_data;
th_union_find m_find;
trail_stack m_trail_stack;
@ -90,8 +92,9 @@ namespace smt {
bool oc_cycle_free(enode * n) const { return n->get_root()->is_marked2(); }
void oc_push_stack(enode * n);
ptr_vector<enode> m_array_args;
ptr_vector<enode> m_args, m_todo;
ptr_vector<enode> const& get_array_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n);
// class for managing state of final_check
class final_check_st {

View file

@ -1539,6 +1539,8 @@ public:
return FC_CONTINUE;
case l_undef:
TRACE("arith", tout << "check-lia giveup\n";);
if (ctx().get_fparams().m_arith_ignore_int)
return FC_GIVEUP;
st = FC_CONTINUE;
break;
}
@ -1866,7 +1868,11 @@ public:
return l_undef;
}
lbool lia_check = l_undef;
switch (m_lia->check(&m_explanation)) {
auto cr = m_lia->check(&m_explanation);
if (cr != lp::lia_move::sat && ctx().get_fparams().m_arith_ignore_int)
return l_undef;
switch (cr) {
case lp::lia_move::sat:
lia_check = l_true;
break;
@ -1896,6 +1902,8 @@ public:
break;
}
case lp::lia_move::cut: {
if (ctx().get_fparams().m_arith_ignore_int)
return l_undef;
TRACE("arith", tout << "cut\n";);
++m_stats.m_gomory_cuts;
// m_explanation implies term <= k
@ -3151,11 +3159,15 @@ public:
// lp().shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed
++m_num_conflicts;
++m_stats.m_conflicts;
TRACE("arith", tout << "scope: " << ctx().get_scope_level() << "\n"; display_evidence(tout, m_explanation); );
TRACE("arith", display(tout << "is-conflict: " << is_conflict << "\n"););
for (auto ev : m_explanation) {
TRACE("arith",
tout << "lemma scope: " << ctx().get_scope_level();
for (auto const& p : m_params) tout << " " << p;
tout << "\n";
display_evidence(tout, m_explanation);
display(tout << "is-conflict: " << is_conflict << "\n"););
for (auto ev : m_explanation)
set_evidence(ev.ci(), m_core, m_eqs);
}
// SASSERT(validate_conflict(m_core, m_eqs));
dump_conflict(m_core, m_eqs);
if (is_conflict) {

View file

@ -1598,10 +1598,12 @@ namespace smt {
lbool is_sat = k.check();
validating = false;
// std::cout << is_sat << "\n";
#if 0
if (is_sat == l_true) {
std::cout << A << "\n";
std::cout << B << "\n";
}
#endif
SASSERT(is_sat != l_true);
return true;
}

View file

@ -467,6 +467,15 @@ namespace smt {
return found;
}
/**
* n is an argument of p, if p is a function definition or case predicate,
* then there is no reason for the solver to enforce that equality on n is
* fully determined. It is a beta-redex with respect to expanding p.
*/
bool theory_recfun::is_beta_redex(enode* p, enode* n) const {
return is_defined(p) || is_case_pred(p);
}
void theory_recfun::display(std::ostream & out) const {
out << "recfun\n";
out << "disabled guards:\n" << m_disabled_guards << "\n";

View file

@ -99,6 +99,7 @@ namespace smt {
bool can_propagate() override;
void propagate() override;
bool should_research(expr_ref_vector &) override;
bool is_beta_redex(enode* p, enode* n) const override;
void new_eq_eh(theory_var v1, theory_var v2) override {}
void new_diseq_eh(theory_var v1, theory_var v2) override {}

View file

@ -1455,7 +1455,7 @@ bool theory_seq::internalize_term(app* term) {
if (ctx.e_internalized(term)) {
mk_var(ctx.get_enode(term));
return true;
}
}
if (m.is_bool(term) &&
(m_util.str.is_in_re(term) || m_sk.is_skolem(term))) {
@ -1484,12 +1484,21 @@ bool theory_seq::internalize_term(app* term) {
mk_var(e);
if (!ctx.relevancy()) {
relevant_eh(term);
}
}
return true;
}
bool theory_seq::is_beta_redex(enode* p, enode* n) const {
expr* term = p->get_expr();
if (!m_util.str.is_map(term) && !m_util.str.is_mapi(term) &&
!m_util.str.is_foldl(term) && !m_util.str.is_foldli(term))
return false;
if (p->get_arg(0)->get_root() == n->get_root())
return true;
return false;
}
void theory_seq::add_length(expr* l) {
expr* e = nullptr;
VERIFY(m_util.str.is_length(l, e));
@ -3273,7 +3282,7 @@ bool theory_seq::should_research(expr_ref_vector & unsat_core) {
k_min *= 2;
if (m_util.is_seq(s_min))
k_min = std::max(m_util.str.min_length(s_min), k_min);
IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-length " << mk_pp(s_min, m) << " " << k_min << ")\n");
IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-length " << mk_bounded_pp(s_min, m, 3) << " " << k_min << ")\n");
add_length_limit(s_min, k_min, false);
return true;
}

View file

@ -412,6 +412,7 @@ namespace smt {
void finalize_model(model_generator & mg) override;
void init_search_eh() override;
void validate_model(model& mdl) override;
bool is_beta_redex(enode* p, enode* n) const override;
void init_model(expr_ref_vector const& es);
app* get_ite_value(expr* a);

View file

@ -36,7 +36,7 @@ namespace smt {
if (!m_next) {
sort* s = decl()->get_domain(0);
sort* domain[2] = {s, s};
m_next = m.mk_fresh_func_decl("next", "", 2, domain, s);
m_next = m.mk_fresh_func_decl("specrel.next", "", 2, domain, s, false);
}
return m_next;
}

View file

@ -201,7 +201,7 @@ namespace smt {
}
void theory_str::assert_axiom(expr * _e) {
if (_e == nullptr)
if (_e == nullptr)
return;
if (opt_VerifyFinalCheckProgress) {
finalCheckProgressIndicator = true;
@ -1100,9 +1100,10 @@ namespace smt {
TRACE("str", tout << "instantiate CharAt axiom for " << mk_pp(expr, m) << std::endl;);
expr_ref ts0(mk_str_var("ts0"), m);
expr_ref ts1(mk_str_var("ts1"), m);
expr_ref ts2(mk_str_var("ts2"), m);
// change subvaribale names to solve some invalide model problems
expr_ref ts0(mk_str_var("ch_ts0"), m);
expr_ref ts1(mk_str_var("ch_ts1"), m);
expr_ref ts2(mk_str_var("ch_ts2"), m);
expr_ref cond(m.mk_and(
m_autil.mk_ge(arg1, mk_int(0)),
@ -1134,8 +1135,9 @@ namespace smt {
TRACE("str", tout << "instantiate prefixof axiom for " << mk_pp(expr, m) << std::endl;);
expr_ref ts0(mk_str_var("ts0"), m);
expr_ref ts1(mk_str_var("ts1"), m);
// change subvaribale names to solve some invalide model problems
expr_ref ts0(mk_str_var("p_ts0"), m);
expr_ref ts1(mk_str_var("p_ts1"), m);
expr_ref_vector innerItems(m);
innerItems.push_back(ctx.mk_eq_atom(expr->get_arg(1), mk_concat(ts0, ts1)));
@ -1170,8 +1172,9 @@ namespace smt {
TRACE("str", tout << "instantiate suffixof axiom for " << mk_pp(expr, m) << std::endl;);
expr_ref ts0(mk_str_var("ts0"), m);
expr_ref ts1(mk_str_var("ts1"), m);
// change subvaribale names to solve some invalide model problems
expr_ref ts0(mk_str_var("s_ts0"), m);
expr_ref ts1(mk_str_var("s_ts1"), m);
expr_ref_vector innerItems(m);
innerItems.push_back(ctx.mk_eq_atom(expr->get_arg(1), mk_concat(ts0, ts1)));
@ -1235,8 +1238,9 @@ namespace smt {
TRACE("str", tout << "instantiate Contains axiom for " << mk_pp(ex, m) << std::endl;);
expr_ref ts0(mk_str_var("ts0"), m);
expr_ref ts1(mk_str_var("ts1"), m);
// change subvaribale names to solve some invalide model problems
expr_ref ts0(mk_str_var("c_ts0"), m);
expr_ref ts1(mk_str_var("c_ts1"), m);
expr_ref breakdownAssert(ctx.mk_eq_atom(ex, ctx.mk_eq_atom(ex->get_arg(0), mk_concat(ts0, mk_concat(ex->get_arg(1), ts1)))), m);
SASSERT(breakdownAssert);
@ -1287,8 +1291,9 @@ namespace smt {
TRACE("str", tout << "instantiate str.indexof axiom for " << mk_pp(ex, m) << std::endl;);
expr_ref x1(mk_str_var("x1"), m);
expr_ref x2(mk_str_var("x2"), m);
// change subvaribale names to solve some invalide model problems
expr_ref x1(mk_str_var("i_x1"), m);
expr_ref x2(mk_str_var("i_x2"), m);
expr_ref condAst1(mk_contains(exHaystack, exNeedle), m);
expr_ref condAst2(m.mk_not(ctx.mk_eq_atom(exNeedle, mk_string(""))), m);
@ -1305,8 +1310,9 @@ namespace smt {
// args[0] = x3 . x4
// /\ |x3| = |x1| + |args[1]| - 1
// /\ ! contains(x3, args[1])
expr_ref x3(mk_str_var("x3"), m);
expr_ref x4(mk_str_var("x4"), m);
// change subvaribale names to solve some invalide model problems
expr_ref x3(mk_str_var("i_x3"), m);
expr_ref x4(mk_str_var("i_x4"), m);
expr_ref tmpLen(m_autil.mk_add(ex, mk_strlen(ex->get_arg(1)), mk_int(-1)), m);
SASSERT(tmpLen);
thenItems.push_back(ctx.mk_eq_atom(exHaystack, mk_concat(x3, x4)));
@ -1501,8 +1507,9 @@ namespace smt {
TRACE("str", tout << "instantiate LastIndexof axiom for " << mk_pp(expr, m) << std::endl;);
expr_ref x1(mk_str_var("x1"), m);
expr_ref x2(mk_str_var("x2"), m);
// change subvaribale names to solve some invalide model problems
expr_ref x1(mk_str_var("li_x1"), m);
expr_ref x2(mk_str_var("li_x2"), m);
expr_ref indexAst(mk_int_var("index"), m);
expr_ref_vector items(m);
@ -1532,8 +1539,9 @@ namespace smt {
if (!canSkip) {
// args[0] = x3 . x4 /\ |x3| = |x1| + 1 /\ ! contains(x4, args[1])
expr_ref x3(mk_str_var("x3"), m);
expr_ref x4(mk_str_var("x4"), m);
// change subvaribale names to solve some invalide model problems
expr_ref x3(mk_str_var("li_x3"), m);
expr_ref x4(mk_str_var("li_x4"), m);
expr_ref tmpLen(m_autil.mk_add(indexAst, mk_int(1)), m);
thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x3, x4)));
thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen));
@ -1690,10 +1698,11 @@ namespace smt {
TRACE("str", tout << "instantiate Replace axiom for " << mk_pp(ex, m) << std::endl;);
expr_ref x1(mk_str_var("x1"), m);
expr_ref x2(mk_str_var("x2"), m);
// change subvaribale names to solve some invalide model problems
expr_ref x1(mk_str_var("rp_x1"), m);
expr_ref x2(mk_str_var("rp_x2"), m);
expr_ref i1(mk_int_var("i1"), m);
expr_ref result(mk_str_var("result"), m);
expr_ref result(mk_str_var("rp_result"), m);
expr * replaceS = nullptr;
expr * replaceT = nullptr;
@ -1714,8 +1723,9 @@ namespace smt {
// i1 = |x1|
thenItems.push_back(ctx.mk_eq_atom(i1, mk_strlen(x1)));
// args[0] = x3 . x4 /\ |x3| = |x1| + |args[1]| - 1 /\ ! contains(x3, args[1])
expr_ref x3(mk_str_var("x3"), m);
expr_ref x4(mk_str_var("x4"), m);
// change subvaribale names to solve some invalide model problems
expr_ref x3(mk_str_var("rp_x3"), m);
expr_ref x4(mk_str_var("rp_x4"), m);
expr_ref tmpLen(m_autil.mk_add(i1, mk_strlen(ex->get_arg(1)), mk_int(-1)), m);
thenItems.push_back(ctx.mk_eq_atom(ex->get_arg(0), mk_concat(x3, x4)));
thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen));
@ -1812,7 +1822,7 @@ namespace smt {
expr_ref zero(mk_string("0"), m);
// let (the result starts with a "0") be p
expr_ref starts_with_zero(u.str.mk_prefix(zero, ex), m);
// let (the result is "0") be q
// let (the result is "0") be q
expr_ref is_zero(ctx.mk_eq_atom(ex, zero), m);
// encoding: the result does NOT start with a "0" (~p) xor the result is "0" (q)
// ~p xor q == (~p or q) and (p or ~q)
@ -1847,7 +1857,7 @@ namespace smt {
expr_ref axiom(ctx.mk_eq_atom(ex, rhs), m);
assert_axiom_rw(axiom);
}
void theory_str::instantiate_axiom_str_from_code(enode * e) {
ast_manager & m = get_manager();
@ -3245,7 +3255,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
assert_implication(ax_l, m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
assert_implication(ax_l, new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
} else if (splitType == 1) {
@ -3303,7 +3317,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
assert_implication(ax_l, m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
assert_implication(ax_l, new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
@ -3355,7 +3373,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
arrangement_disjunction.push_back(new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
@ -3400,7 +3422,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
arrangement_disjunction.push_back(new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
@ -3641,7 +3667,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
assert_implication(ax_l, m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
assert_implication(ax_l, new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
@ -3742,7 +3772,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
arrangement_disjunction.push_back(new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
}
@ -4037,7 +4071,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
assert_implication(ax_l, m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
assert_implication(ax_l, new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
}
@ -4116,7 +4154,11 @@ namespace smt {
if (!overlapAssumptionUsed) {
overlapAssumptionUsed = true;
arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
arrangement_disjunction.push_back(new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
}
}
}
@ -4513,7 +4555,11 @@ namespace smt {
// only add the overlap assumption one time
if (!overlapAssumptionUsed) {
arrangement_disjunction.push_back(m_theoryStrOverlapAssumption_term);
// add context dependent formula overlap predicate and relate it to the global overlap predicate
sort * s = get_manager().mk_bool_sort();
expr_ref new_OverlapAssumption_term = expr_ref(mk_fresh_const(newOverlapStr, s), get_manager());
arrangement_disjunction.push_back(new_OverlapAssumption_term);
assert_implication(new_OverlapAssumption_term, m_theoryStrOverlapAssumption_term);
overlapAssumptionUsed = true;
}
@ -4577,7 +4623,7 @@ namespace smt {
u.str.is_string(strExpr, stringVal);
return true;
}
/*
* Look through the equivalence class of n to find a string constant.
* Return that constant if it is found, and set hasEqcValue to true.
@ -4604,7 +4650,7 @@ namespace smt {
return a;
}
curr = m_find.next(curr);
}
}
while (curr != first && curr != null_theory_var);
}
hasEqcValue = false;
@ -4781,10 +4827,13 @@ namespace smt {
//} else if (getNodeType(t, node) == my_Z3_Func) {
} else if (is_app(node)) {
app * func_app = to_app(node);
unsigned int argCount = func_app->get_num_args();
for (unsigned int i = 0; i < argCount; i++) {
expr * argAst = func_app->get_arg(i);
get_const_str_asts_in_node(argAst, astList);
// the following check is only valid when the operator is string concatenate
if (u.str.is_concat(func_app)) {
unsigned int argCount = func_app->get_num_args();
for (unsigned int i = 0; i < argCount; i++) {
expr * argAst = func_app->get_arg(i);
get_const_str_asts_in_node(argAst, astList);
}
}
}
}
@ -6885,7 +6934,7 @@ namespace smt {
}
// heuristics
if (u.str.is_prefix(e)) {
check_consistency_prefix(e, is_true);
} else if (u.str.is_suffix(e)) {
@ -6905,7 +6954,7 @@ namespace smt {
VERIFY(u.str.is_prefix(e, needle, haystack));
TRACE("str", tout << "check consistency of prefix predicate: " << mk_pp(needle, m) << " prefixof " << mk_pp(haystack, m) << std::endl;);
zstring needleStringConstant;
if (get_string_constant_eqc(needle, needleStringConstant)) {
if (u.str.is_itos(haystack) && is_true) {
@ -6932,7 +6981,7 @@ namespace smt {
VERIFY(u.str.is_suffix(e, needle, haystack));
TRACE("str", tout << "check consistency of suffix predicate: " << mk_pp(needle, m) << " suffixof " << mk_pp(haystack, m) << std::endl;);
zstring needleStringConstant;
if (get_string_constant_eqc(needle, needleStringConstant)) {
if (u.str.is_itos(haystack) && is_true) {
@ -6959,7 +7008,7 @@ namespace smt {
VERIFY(u.str.is_contains(e, haystack, needle)); // first string contains second one
TRACE("str", tout << "check consistency of contains predicate: " << mk_pp(haystack, m) << " contains " << mk_pp(needle, m) << std::endl;);
zstring needleStringConstant;
if (get_string_constant_eqc(needle, needleStringConstant)) {
if (u.str.is_itos(haystack) && is_true) {
@ -7052,7 +7101,7 @@ namespace smt {
m_concat_eval_todo.reset();
m_delayed_axiom_setup_terms.reset();
m_delayed_assertions_todo.reset();
TRACE_CODE(if (is_trace_enabled("t_str_dump_assign_on_scope_change")) { dump_assignments(); });
// list of expr* to remove from cut_var_map
@ -8386,7 +8435,7 @@ namespace smt {
}
}
}
if (!needToAssignFreeVars) {
// check string-int terms
@ -8685,7 +8734,7 @@ namespace smt {
} else if (u.str.is_itos(ex)) {
expr* fromInt = nullptr;
u.str.is_itos(ex, fromInt);
arith_value v(m);
v.init(&ctx);
rational val;
@ -8808,7 +8857,7 @@ namespace smt {
if (!u.str.is_string(to_app(Gamma.get(left_count)))) {
rational offsetLen = offset - left_length + 1;
extra_left_cond = m_autil.mk_ge(u.str.mk_length(Gamma.get(left_count)), mk_int(offsetLen));
}
}
// find len(Delta[:j])
unsigned right_count = 0;
@ -8887,7 +8936,7 @@ namespace smt {
expr* theory_str::refine_dis(expr* lhs, expr* rhs) {
ast_manager & m = get_manager();
expr_ref lesson(m);
lesson = m.mk_not(m.mk_eq(lhs, rhs));
TRACE("str", tout << "learning not " << mk_pp(lesson, m) << std::endl;);

View file

@ -393,6 +393,8 @@ protected:
// does not introduce equalities when they weren't enforced.
unsigned m_unused_id;
const char* newOverlapStr = "!!NewOverlapAssumption!!";
// terms we couldn't go through set_up_axioms() with because they weren't internalized
expr_ref_vector m_delayed_axiom_setup_terms;
@ -492,7 +494,7 @@ protected:
obj_map<expr, std::tuple<rational, expr*, expr*>> fixed_length_lesson; //keep track of information for the lesson
unsigned preprocessing_iteration_count; // number of attempts we've made to solve by preprocessing length information
obj_map<expr, zstring> candidate_model;
stats m_stats;
protected:
@ -777,4 +779,3 @@ protected:
};
};

View file

@ -20,6 +20,7 @@ Author:
#include "smt/theory_bv.h"
#include "smt/theory_user_propagator.h"
#include "smt/smt_context.h"
#include "ast/ast_ll_pp.h"
using namespace smt;
@ -49,6 +50,7 @@ void theory_user_propagator::add_expr(expr* term, bool ensure_enode) {
expr_ref r(m);
expr* e = term;
ctx.get_rewriter()(e, r);
TRACE("user_propagate", tout << "add " << mk_bounded_pp(e, m) << "\n");
if (r != e) {
r = m.mk_fresh_const("aux-expr", e->get_sort());
expr_ref eq(m.mk_eq(r, e), m);
@ -102,6 +104,17 @@ void theory_user_propagator::register_cb(expr* e) {
add_expr(e, true);
}
void theory_user_propagator::next_split_cb(expr* e, unsigned idx, lbool phase) {
if (e == nullptr) { // clear
m_next_split_expr = nullptr;
return;
}
ensure_enode(e);
m_next_split_expr = e;
m_next_split_idx = idx;
m_next_split_phase = phase;
}
theory * theory_user_propagator::mk_fresh(context * new_ctx) {
auto* th = alloc(theory_user_propagator, *new_ctx);
void* ctx;
@ -156,40 +169,71 @@ void theory_user_propagator::new_fixed_eh(theory_var v, expr* value, unsigned nu
}
}
bool_var theory_user_propagator::enode_to_bool(enode* n, unsigned bit) {
if (n->is_bool()) {
// expression is a boolean
bool_var new_var = ctx.enode2bool_var(n);
if (ctx.get_assignment(new_var) == l_undef)
return new_var;
return null_bool_var;
}
// expression is a bit-vector
bv_util bv(m);
auto th_bv = (theory_bv*)ctx.get_theory(bv.get_fid());
return th_bv->get_first_unassigned(bit, n);
}
void theory_user_propagator::decide(bool_var& var, bool& is_pos) {
if (!m_decide_eh)
return;
const bool_var_data& d = ctx.get_bdata(var);
if (!d.is_theory_atom())
if (!d.is_enode() && !d.is_theory_atom())
return;
enode* original_enode = nullptr;
unsigned original_bit = 0;
bv_util bv(m);
theory* th = nullptr;
theory_var v = null_theory_var;
// get the associated theory
if (!d.is_enode()) {
// it might be a value that does not have an enode
th = ctx.get_theory(d.get_theory());
}
else {
original_enode = ctx.bool_var2enode(var);
v = original_enode->get_th_var(get_family_id());
if (v == null_theory_var) {
// it is not a registered boolean expression
th = ctx.get_theory(d.get_theory());
}
}
if (v == null_theory_var && !th)
return;
theory* th = ctx.get_theory(d.get_theory());
if (v == null_theory_var && th->get_family_id() != bv.get_fid())
return;
bv_util bv(m);
enode* original_enode = nullptr;
unsigned original_bit = 0;
if (d.is_enode() && th->get_family_id() == get_family_id()) {
// variable is just a registered expression
original_enode = ctx.bool_var2enode(var);
}
else if (th->get_family_id() == bv.get_fid()) {
// it might be a registered bit-vector
if (v == null_theory_var) {
// it is not a registered boolean value but it is a bitvector
auto registered_bv = ((theory_bv*)th)->get_bv_with_theory(var, get_family_id());
if (!registered_bv.first)
// there is no registered bv associated with the bit
return;
original_enode = registered_bv.first;
original_bit = registered_bv.second;
v = original_enode->get_th_var(get_family_id());
}
else
return;
// call the registered callback
unsigned new_bit = original_bit;
lbool phase = is_pos ? l_true : l_false;
expr* e = var2expr(original_enode->get_th_var(get_family_id()));
expr* e = var2expr(v);
m_decide_eh(m_user_context, this, &e, &new_bit, &phase);
enode* new_enode = ctx.get_enode(e);
@ -201,28 +245,28 @@ void theory_user_propagator::decide(bool_var& var, bool& is_pos) {
return;
}
bool_var old_var = var;
if (new_enode->is_bool()) {
// expression was set to a boolean
bool_var new_var = ctx.enode2bool_var(new_enode);
if (ctx.get_assignment(new_var) == l_undef) {
var = new_var;
}
}
else {
// expression was set to a bit-vector
auto th_bv = (theory_bv*)ctx.get_theory(bv.get_fid());
bool_var new_var = th_bv->get_first_unassigned(new_bit, new_enode);
if (new_var != null_bool_var) {
var = new_var;
}
}
// get unassigned variable from enode
var = enode_to_bool(new_enode, new_bit);
// in case the callback did not decide on a truth value -> let Z3 decide
is_pos = ctx.guess(var, phase);
}
bool theory_user_propagator::get_case_split(bool_var& var, bool& is_pos){
if (!m_next_split_expr)
return false;
enode* n = ctx.get_enode(m_next_split_expr);
var = enode_to_bool(n, m_next_split_idx);
if (var == null_bool_var)
return false;
is_pos = ctx.guess(var, m_next_split_phase);
m_next_split_expr = nullptr;
return true;
}
void theory_user_propagator::push_scope_eh() {
++m_num_scopes;
}

View file

@ -83,6 +83,9 @@ namespace smt {
expr_ref_vector m_to_add;
unsigned_vector m_to_add_lim;
unsigned m_to_add_qhead = 0;
expr* m_next_split_expr = nullptr;
unsigned m_next_split_idx;
lbool m_next_split_phase;
expr* var2expr(theory_var v) { return m_var2expr.get(v); }
theory_var expr2var(expr* e) { check_defined(e); return m_expr2var[e->get_id()]; }
@ -95,6 +98,8 @@ namespace smt {
void propagate_consequence(prop_info const& prop);
void propagate_new_fixed(prop_info const& prop);
bool_var enode_to_bool(enode* n, unsigned bit);
public:
theory_user_propagator(context& ctx);
@ -125,13 +130,14 @@ namespace smt {
void register_decide(user_propagator::decide_eh_t& decide_eh) { m_decide_eh = decide_eh; }
bool has_fixed() const { return (bool)m_fixed_eh; }
bool has_decide() const { return (bool)m_decide_eh; }
void propagate_cb(unsigned num_fixed, expr* const* fixed_ids, unsigned num_eqs, expr* const* lhs, expr* const* rhs, expr* conseq) override;
void register_cb(expr* e) override;
void next_split_cb(expr* e, unsigned idx, lbool phase) override;
void new_fixed_eh(theory_var v, expr* value, unsigned num_lits, literal const* jlits);
void decide(bool_var& var, bool& is_pos);
bool get_case_split(bool_var& var, bool& is_pos);
theory * mk_fresh(context * new_ctx) override;
bool internalize_atom(app* atom, bool gate_ctx) override;
@ -154,5 +160,5 @@ namespace smt {
bool can_propagate() override;
void propagate() override;
void display(std::ostream& out) const override {}
};
};
};