mirror of
https://github.com/Z3Prover/z3
synced 2025-08-26 04:56:03 +00:00
Parallel solving (#7771)
* very basic setup * ensure solve_eqs is fully disabled when smt.solve_eqs=false, #7743 Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * respect smt configuration parameter in elim_unconstrained simplifier Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * indentation * add bash files for test runs * add option to selectively disable variable solving for only ground expressions Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove verbose output Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix #7745 axioms for len(substr(...)) escaped due to nested rewriting * ensure atomic constraints are processed by arithmetic solver * #7739 optimization add simplification rule for at(x, offset) = "" Introducing j just postpones some rewrites that prevent useful simplifications. Z3 already uses common sub-expressions. The example highlights some opportunities for simplification, noteworthy at(..) = "". The example is solved in both versions after adding this simplification. * fix unsound len(substr) axiom Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * FreshConst is_sort (#7748) * #7750 add pre-processing simplification * Add parameter validation for selected API functions * updates to ac-plugin fix incrementality bugs by allowing destructive updates during saturation at the cost of redoing saturation after a pop. * enable passive, add check for bloom up-to-date * add top-k fixed-sized min-heap priority queue for top scoring literals * set up worker thread batch manager for multithreaded batch cubes paradigm, need to debug as I am getting segfault still * fix bug in parallel solving batch setup * fix bug * allow for internalize implies * disable pre-processing during cubing * debugging * remove default constructor * remove a bunch of string copies * Update euf_ac_plugin.cpp include reduction rules in forward simplification * Update euf_completion.cpp try out restricting scope of equalities added by instantation * Update smt_parallel.cpp Drop non-relevant units from shared structures. * process cubes as lists of individual lits * merge * Add support for Algebraic Datatypes in JavaScript/TypeScript bindings (#7734) * Initial plan * Add datatype type definitions to types.ts (work in progress) Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Complete datatype type definitions with working TypeScript compilation Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Implement core datatype functionality with TypeScript compilation success Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Complete datatype implementation with full Context integration and tests Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * chipping away at the new code structure * comments * debug infinite recursion and split cubes on existing split atoms that aren't in the cube * share lemmas, learn from unsat core, try to debug a couple of things, there was a subtle bug that i have a hard time repro'ing * merge * fix #7603: race condition in Ctrl-C handling (#7755) * fix #7603: race condition in Ctrl-C handling * fix race in cancel_eh * fix build * add arithemtic saturation * add an option to register callback on quantifier instantiation Suppose a user propagator encodes axioms using quantifiers and uses E-matching for instantiation. If it wants to implement a custom priority scheme or drop some instances based on internal checks it can register a callback with quantifier instantiation * missing new closure Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add Z3_solver_propagate_on_binding to ml callback declarations Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add python file Signed-off-by: Lev Nachmanson <levnach@Levs-MacBook-Pro.local> * debug under defined calls Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * more untangle params Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * precalc parameters to define the eval order Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * remove a printout Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * rename a Python file Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * add on_binding callbacks across APIs update release notes, add to Java, .Net, C++ * use jboolean in Native interface Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * register on_binding attribute Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix java build for java bindings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * avoid interferring side-effects in function calls Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove theory_str and classes that are only used by it * remove automata from python build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove ref to theory_str Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * get the finest factorizations before project Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * rename add_lcs to add_lc Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * resolve bad bug about l2g and g2l translators using wrong global context. add some debug prints * initial attempt at dynamically switching from greedy to frugal splitting strategy in return_cubes. need to test. also there is some bug where the threads take forever to cancel? * Update RELEASE_NOTES.md * resolve bug about not translating managers correctly for the second phase of the greedy cubing, and the frugal fallback --------- Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> Signed-off-by: Lev Nachmanson <levnach@Levs-MacBook-Pro.local> Signed-off-by: Lev Nachmanson <levnach@hotmail.com> Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com> Co-authored-by: humnrdble <83878671+humnrdble@users.noreply.github.com> Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> Co-authored-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
parent
2169364b6d
commit
6044389446
80 changed files with 474 additions and 15087 deletions
|
@ -68,10 +68,7 @@ z3_add_component(smt
|
|||
theory_recfun.cpp
|
||||
theory_seq.cpp
|
||||
theory_sls.cpp
|
||||
theory_special_relations.cpp
|
||||
theory_str.cpp
|
||||
theory_str_mc.cpp
|
||||
theory_str_regex.cpp
|
||||
theory_special_relations.cpp
|
||||
theory_user_propagator.cpp
|
||||
theory_utvpi.cpp
|
||||
theory_wmaxsat.cpp
|
||||
|
|
|
@ -263,6 +263,11 @@ namespace smt {
|
|||
if (stat->get_num_instances() % m_params.m_qi_profile_freq == 0) {
|
||||
m_qm.display_stats(verbose_stream(), q);
|
||||
}
|
||||
|
||||
if (m_on_binding && !m_on_binding(q, instance)) {
|
||||
verbose_stream() << "qi_queue: on_binding returned false, skipping instance.\n";
|
||||
return;
|
||||
}
|
||||
expr_ref lemma(m);
|
||||
if (m.is_or(s_instance)) {
|
||||
ptr_vector<expr> args;
|
||||
|
|
|
@ -28,6 +28,7 @@ Revision History:
|
|||
#include "params/qi_params.h"
|
||||
#include "ast/cost_evaluator.h"
|
||||
#include "util/statistics.h"
|
||||
#include "tactic/user_propagator_base.h"
|
||||
|
||||
namespace smt {
|
||||
class context;
|
||||
|
@ -52,6 +53,7 @@ namespace smt {
|
|||
cached_var_subst m_subst;
|
||||
svector<float> m_vals;
|
||||
double m_eager_cost_threshold = 0;
|
||||
std::function<bool(quantifier*,expr*)> m_on_binding;
|
||||
struct entry {
|
||||
fingerprint * m_qb;
|
||||
float m_cost;
|
||||
|
@ -95,6 +97,9 @@ namespace smt {
|
|||
void reset();
|
||||
void display_delayed_instances_stats(std::ostream & out) const;
|
||||
void collect_statistics(::statistics & st) const;
|
||||
void register_on_binding(std::function<bool(quantifier* q, expr* e)> & on_binding) {
|
||||
m_on_binding = on_binding;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -459,7 +459,8 @@ bool theory_seq::branch_binary_variable(depeq const& e) {
|
|||
}
|
||||
if (lenX + rational(xs.size()) != lenY + rational(ys.size())) {
|
||||
// |x| - |y| = |ys| - |xs|
|
||||
expr_ref a(mk_sub(mk_len(x), mk_len(y)), m);
|
||||
auto p0 = mk_len(x);
|
||||
expr_ref a(mk_sub(p0, mk_len(y)), m);
|
||||
expr_ref b(m_autil.mk_int(rational(ys.size())-rational(xs.size())), m);
|
||||
propagate_lit(e.dep(), 0, nullptr, mk_eq(a, b, false));
|
||||
return true;
|
||||
|
@ -702,7 +703,8 @@ bool theory_seq::branch_quat_variable(depeq const& e) {
|
|||
|
||||
literal_vector lits;
|
||||
if (xs == ys) {
|
||||
literal lit = mk_eq(mk_len(x1), mk_len(y1), false);
|
||||
auto p0 = mk_len(x1);
|
||||
literal lit = mk_eq(p0, mk_len(y1), false);
|
||||
if (ctx.get_assignment(lit) == l_undef) {
|
||||
TRACE(seq, tout << mk_pp(x1, m) << " = " << mk_pp(y1, m) << "?\n";);
|
||||
ctx.mark_as_relevant(lit);
|
||||
|
@ -1007,7 +1009,8 @@ bool theory_seq::propagate_length_coherence(expr* e) {
|
|||
// len(e) <= hi => len(tail) <= hi - lo
|
||||
expr_ref high1(m_autil.mk_le(len_e, m_autil.mk_numeral(hi, true)), m);
|
||||
if (hi == lo) {
|
||||
add_axiom(~mk_literal(high1), mk_seq_eq(seq, emp));
|
||||
auto p0 = ~mk_literal(high1);
|
||||
add_axiom(p0, mk_seq_eq(seq, emp));
|
||||
added = true;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1838,6 +1838,14 @@ namespace smt {
|
|||
m_user_propagator->register_decide(r);
|
||||
}
|
||||
|
||||
void user_propagate_register_on_binding(user_propagator::binding_eh_t& t) {
|
||||
m_user_propagator->register_on_binding(t);
|
||||
}
|
||||
|
||||
void register_on_binding(std::function<bool(quantifier* q, expr* inst)>& f) {
|
||||
m_qmanager->register_on_binding(f);
|
||||
}
|
||||
|
||||
void user_propagate_initialize_value(expr* var, expr* value);
|
||||
|
||||
bool watches_fixed(enode* n) const;
|
||||
|
|
|
@ -307,6 +307,10 @@ namespace smt {
|
|||
void kernel::user_propagate_register_fixed(user_propagator::fixed_eh_t& fixed_eh) {
|
||||
m_imp->m_kernel.user_propagate_register_fixed(fixed_eh);
|
||||
}
|
||||
|
||||
void kernel::user_propagate_register_on_binding(user_propagator::binding_eh_t& on_binding) {
|
||||
m_imp->m_kernel.user_propagate_register_on_binding(on_binding);
|
||||
}
|
||||
|
||||
void kernel::user_propagate_register_final(user_propagator::final_eh_t& final_eh) {
|
||||
m_imp->m_kernel.user_propagate_register_final(final_eh);
|
||||
|
|
|
@ -319,6 +319,8 @@ namespace smt {
|
|||
|
||||
void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh);
|
||||
|
||||
void user_propagate_register_on_binding(user_propagator::binding_eh_t& binding_eh);
|
||||
|
||||
void user_propagate_register_expr(expr* e);
|
||||
|
||||
void user_propagate_register_created(user_propagator::created_eh_t& r);
|
||||
|
|
|
@ -44,6 +44,7 @@ namespace smt {
|
|||
ast_translation g2l(p.ctx.m, m); // global to local context -- MUST USE p.ctx.m, not ctx->m, AS GLOBAL MANAGER!!!
|
||||
ast_translation l2g(m, p.ctx.m); // local to global context
|
||||
while (m.inc()) {
|
||||
IF_VERBOSE(0, verbose_stream() << "Worker " << id << " checking cubes\n");
|
||||
vector<expr_ref_vector> cubes;
|
||||
b.get_cubes(g2l, cubes);
|
||||
if (cubes.empty())
|
||||
|
@ -363,7 +364,7 @@ namespace smt {
|
|||
if (greedy_mode) {
|
||||
// Start as greedy: split all cubes on new atoms
|
||||
for (; a_worker_start_idx < A_worker.size(); ++a_worker_start_idx) {
|
||||
expr_ref g_atom(A_worker[a_worker_start_idx], l2g.to());
|
||||
expr_ref g_atom(l2g(A_worker[a_worker_start_idx]), l2g.to());
|
||||
if (m_split_atoms.contains(g_atom))
|
||||
continue;
|
||||
m_split_atoms.push_back(g_atom);
|
||||
|
@ -381,7 +382,7 @@ namespace smt {
|
|||
if (!greedy_mode) {
|
||||
// Split only cubes added in *this call* on the new A_worker atoms (starting where we left off from the initial greedy phase)
|
||||
for (unsigned i = a_worker_start_idx; i < A_worker.size(); ++i) {
|
||||
expr_ref g_atom(A_worker[i], l2g.to());
|
||||
expr_ref g_atom(l2g(A_worker[i]), l2g.to());
|
||||
if (!m_split_atoms.contains(g_atom))
|
||||
m_split_atoms.push_back(g_atom);
|
||||
add_split_atom(g_atom, initial_m_cubes_size); // start from the initial size of m_cubes to ONLY split the NEW worker cubes
|
||||
|
|
|
@ -339,6 +339,10 @@ namespace smt {
|
|||
m_plugin->add_eq_eh(n1, n2);
|
||||
}
|
||||
|
||||
void register_on_binding(std::function<bool(quantifier*, expr*)>& on_binding) {
|
||||
m_qi_queue.register_on_binding(on_binding);
|
||||
}
|
||||
|
||||
void relevant_eh(enode * n) {
|
||||
m_plugin->relevant_eh(n);
|
||||
}
|
||||
|
@ -493,6 +497,10 @@ namespace smt {
|
|||
m_imp->add_eq_eh(n1, n2);
|
||||
}
|
||||
|
||||
void quantifier_manager::register_on_binding(std::function<bool(quantifier*, expr*)>& on_binding) {
|
||||
m_imp->register_on_binding(on_binding);
|
||||
}
|
||||
|
||||
void quantifier_manager::relevant_eh(enode * n) {
|
||||
m_imp->relevant_eh(n);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ Revision History:
|
|||
#include "util/statistics.h"
|
||||
#include "util/params.h"
|
||||
#include "smt/smt_types.h"
|
||||
#include "tactic/user_propagator_base.h"
|
||||
#include <tuple>
|
||||
|
||||
class proto_model;
|
||||
|
@ -96,6 +97,8 @@ namespace smt {
|
|||
void collect_statistics(::statistics & st) const;
|
||||
void reset_statistics();
|
||||
|
||||
void register_on_binding(std::function<bool(quantifier*, expr*)> & f);
|
||||
|
||||
ptr_vector<quantifier>::const_iterator begin_quantifiers() const;
|
||||
ptr_vector<quantifier>::const_iterator end_quantifiers() const;
|
||||
ptr_vector<quantifier>::const_iterator begin() const { return begin_quantifiers(); }
|
||||
|
|
|
@ -39,7 +39,6 @@ Revision History:
|
|||
#include "smt/theory_sls.h"
|
||||
#include "smt/theory_pb.h"
|
||||
#include "smt/theory_fpa.h"
|
||||
#include "smt/theory_str.h"
|
||||
#include "smt/theory_polymorphism.h"
|
||||
|
||||
namespace smt {
|
||||
|
@ -561,10 +560,7 @@ namespace smt {
|
|||
}
|
||||
|
||||
void setup::setup_QF_S() {
|
||||
if (m_params.m_string_solver == "z3str3") {
|
||||
setup_str();
|
||||
}
|
||||
else if (m_params.m_string_solver == "seq") {
|
||||
if (m_params.m_string_solver == "seq") {
|
||||
setup_unknown();
|
||||
}
|
||||
else if (m_params.m_string_solver == "char") {
|
||||
|
@ -582,7 +578,7 @@ namespace smt {
|
|||
// don't register any solver.
|
||||
}
|
||||
else {
|
||||
throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'");
|
||||
throw default_exception("invalid parameter for smt.string_solver, valid options are 'seq', 'auto'");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -748,10 +744,7 @@ namespace smt {
|
|||
|
||||
void setup::setup_seq_str(static_features const & st) {
|
||||
// check params for what to do here when it's ambiguous
|
||||
if (m_params.m_string_solver == "z3str3") {
|
||||
setup_str();
|
||||
}
|
||||
else if (m_params.m_string_solver == "seq") {
|
||||
if (m_params.m_string_solver == "seq") {
|
||||
setup_seq();
|
||||
}
|
||||
else if (m_params.m_string_solver == "empty") {
|
||||
|
@ -760,16 +753,11 @@ namespace smt {
|
|||
else if (m_params.m_string_solver == "none") {
|
||||
// don't register any solver.
|
||||
}
|
||||
else if (m_params.m_string_solver == "auto") {
|
||||
if (st.m_has_seq_non_str) {
|
||||
else if (m_params.m_string_solver == "auto") {
|
||||
setup_seq();
|
||||
}
|
||||
else {
|
||||
setup_str();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'");
|
||||
throw default_exception("invalid parameter for smt.string_solver, valid options are 'seq', 'auto'");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -787,11 +775,6 @@ namespace smt {
|
|||
m_context.register_plugin(alloc(theory_fpa, m_context));
|
||||
}
|
||||
|
||||
void setup::setup_str() {
|
||||
setup_arith();
|
||||
m_context.register_plugin(alloc(theory_str, m_context, m_manager, m_params));
|
||||
}
|
||||
|
||||
void setup::setup_seq() {
|
||||
m_context.register_plugin(alloc(smt::theory_seq, m_context));
|
||||
setup_char();
|
||||
|
|
|
@ -108,7 +108,6 @@ namespace smt {
|
|||
void setup_mi_arith();
|
||||
void setup_lra_arith();
|
||||
void setup_fpa();
|
||||
void setup_str();
|
||||
void setup_relevancy(static_features& st);
|
||||
|
||||
public:
|
||||
|
|
|
@ -244,6 +244,10 @@ namespace {
|
|||
m_context.user_propagate_register_expr(e);
|
||||
}
|
||||
|
||||
void user_propagate_register_on_binding(user_propagator::binding_eh_t& binding_eh) override {
|
||||
m_context.user_propagate_register_on_binding(binding_eh);
|
||||
}
|
||||
|
||||
void user_propagate_register_created(user_propagator::created_eh_t& c) override {
|
||||
m_context.user_propagate_register_created(c);
|
||||
}
|
||||
|
|
|
@ -283,7 +283,6 @@ namespace smt {
|
|||
/**
|
||||
\brief This method is called by smt_context before the search starts
|
||||
to get any extra assumptions the theory wants to use.
|
||||
(See theory_str for an example)
|
||||
*/
|
||||
virtual void add_theory_assumptions(expr_ref_vector & assumptions) {
|
||||
}
|
||||
|
|
|
@ -402,6 +402,10 @@ public:
|
|||
m_diseq_eh = diseq_eh;
|
||||
}
|
||||
|
||||
void user_propagate_register_on_binding(user_propagator::binding_eh_t& binding_eh) override {
|
||||
m_ctx.load()->user_propagate_register_on_binding(binding_eh);
|
||||
}
|
||||
|
||||
void user_propagate_register_expr(expr* e) override {
|
||||
m_vars.push_back(e);
|
||||
}
|
||||
|
|
|
@ -440,7 +440,11 @@ final_check_status theory_seq::final_check_eh() {
|
|||
|
||||
|
||||
bool theory_seq::set_empty(expr* x) {
|
||||
add_axiom(~mk_eq(m_autil.mk_int(0), mk_len(x), false), mk_eq_empty(x));
|
||||
// pre-calculate literals to enforce evaluation order
|
||||
auto zero = m_autil.mk_int(0);
|
||||
literal zero_len_lit = mk_eq(zero, mk_len(x), false);
|
||||
literal empty_lit = mk_eq_empty(x);
|
||||
add_axiom(~zero_len_lit, empty_lit);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1587,7 +1591,8 @@ void theory_seq::add_length_limit(expr* s, unsigned k, bool is_searching) {
|
|||
m_trail_stack.push(insert_obj_map<expr, unsigned>(m_length_limit_map, s));
|
||||
if (is_searching) {
|
||||
expr_ref dlimit = m_sk.mk_max_unfolding_depth(m_max_unfolding_depth);
|
||||
add_axiom(~mk_literal(dlimit), mk_literal(lim_e));
|
||||
auto p0 = ~mk_literal(dlimit);
|
||||
add_axiom(p0, mk_literal(lim_e));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2905,7 +2910,8 @@ void theory_seq::ensure_nth(literal lit, expr* s, expr* idx) {
|
|||
m_sk.decompose(s2, head, tail);
|
||||
elems.push_back(head);
|
||||
len1 = mk_len(s2);
|
||||
len2 = m_autil.mk_add(m_autil.mk_int(1), mk_len(tail));
|
||||
auto one = m_autil.mk_int(1);
|
||||
len2 = m_autil.mk_add(one, mk_len(tail));
|
||||
propagate_eq(lit, len1, len2, false);
|
||||
s2 = tail;
|
||||
}
|
||||
|
@ -3062,7 +3068,8 @@ void theory_seq::assign_eh(bool_var v, bool is_true) {
|
|||
f = m_sk.mk_prefix_inv(se1, se2);
|
||||
f = mk_concat(se1, f);
|
||||
propagate_eq(lit, f, se2, true);
|
||||
propagate_eq(lit, mk_len(f), mk_len(se2), false);
|
||||
auto p0 = mk_len(f);
|
||||
propagate_eq(lit, p0, mk_len(se2), false);
|
||||
}
|
||||
else {
|
||||
propagate_not_prefix(e);
|
||||
|
@ -3076,7 +3083,8 @@ void theory_seq::assign_eh(bool_var v, bool is_true) {
|
|||
f = m_sk.mk_suffix_inv(se1, se2);
|
||||
f = mk_concat(f, se1);
|
||||
propagate_eq(lit, f, se2, true);
|
||||
propagate_eq(lit, mk_len(f), mk_len(se2), false);
|
||||
auto p0 = mk_len(f);
|
||||
propagate_eq(lit, p0, mk_len(se2), false);
|
||||
}
|
||||
else {
|
||||
propagate_not_suffix(e);
|
||||
|
@ -3096,13 +3104,15 @@ void theory_seq::assign_eh(bool_var v, bool is_true) {
|
|||
expr_ref f2 = m_sk.mk_contains_right(se1, se2);
|
||||
f = mk_concat(f1, se2, f2);
|
||||
propagate_eq(lit, f, e1, true);
|
||||
propagate_eq(lit, mk_len(f), mk_len(e1), false);
|
||||
auto p0 = mk_len(f);
|
||||
propagate_eq(lit, p0, mk_len(e1), false);
|
||||
}
|
||||
else {
|
||||
propagate_non_empty(lit, se2);
|
||||
dependency* dep = m_dm.mk_leaf(assumption(lit));
|
||||
// |e1| - |e2| <= -1
|
||||
literal len_gt = m_ax.mk_le(mk_sub(mk_len(se1), mk_len(se2)), -1);
|
||||
auto e1e = mk_len(se1);
|
||||
literal len_gt = m_ax.mk_le(mk_sub(e1e, mk_len(se2)), -1);
|
||||
ctx.force_phase(len_gt);
|
||||
m_ncs.push_back(nc(expr_ref(e, m), len_gt, dep));
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,779 +0,0 @@
|
|||
/*++
|
||||
Module Name:
|
||||
|
||||
theory_str.h
|
||||
|
||||
Abstract:
|
||||
|
||||
String Theory Plugin
|
||||
|
||||
Author:
|
||||
|
||||
Murphy Berzish and Yunhui Zheng
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/trail.h"
|
||||
#include "util/union_find.h"
|
||||
#include "util/scoped_ptr_vector.h"
|
||||
#include "util/hashtable.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/rewriter/seq_rewriter.h"
|
||||
#include "ast/seq_decl_plugin.h"
|
||||
#include "model/value_factory.h"
|
||||
#include "smt/smt_theory.h"
|
||||
#include "params/theory_str_params.h"
|
||||
#include "smt/smt_model_generator.h"
|
||||
#include "smt/smt_arith_value.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include<set>
|
||||
#include<stack>
|
||||
#include<vector>
|
||||
#include<map>
|
||||
#include<functional>
|
||||
|
||||
namespace smt {
|
||||
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
|
||||
typedef int_hashtable<int_hash, default_eq<int> > integer_set;
|
||||
|
||||
class str_value_factory : public value_factory {
|
||||
seq_util u;
|
||||
symbol_set m_strings;
|
||||
std::string delim;
|
||||
unsigned m_next;
|
||||
public:
|
||||
str_value_factory(ast_manager & m, family_id fid) :
|
||||
value_factory(m, fid),
|
||||
u(m), delim("!"), m_next(0) {}
|
||||
expr * get_some_value(sort * s) override {
|
||||
return u.str.mk_string("some value");
|
||||
}
|
||||
bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override {
|
||||
v1 = u.str.mk_string("value 1");
|
||||
v2 = u.str.mk_string("value 2");
|
||||
return true;
|
||||
}
|
||||
expr * get_fresh_value(sort * s) override {
|
||||
if (u.is_string(s)) {
|
||||
while (true) {
|
||||
std::ostringstream strm;
|
||||
strm << delim << std::hex << (m_next++) << std::dec << delim;
|
||||
std::string s(strm.str());
|
||||
symbol sym(s);
|
||||
if (m_strings.contains(sym)) continue;
|
||||
m_strings.insert(sym);
|
||||
return u.str.mk_string(s);
|
||||
}
|
||||
}
|
||||
sort* seq = nullptr;
|
||||
if (u.is_re(s, seq)) {
|
||||
expr* v0 = get_fresh_value(seq);
|
||||
return u.re.mk_to_re(v0);
|
||||
}
|
||||
TRACE(t_str, tout << "unexpected sort in get_fresh_value(): " << mk_pp(s, m_manager) << std::endl;);
|
||||
UNREACHABLE(); return nullptr;
|
||||
}
|
||||
void register_value(expr * n) override { /* Ignore */ }
|
||||
};
|
||||
|
||||
// NSB: added operator[] and contains to obj_pair_hashtable
|
||||
class theory_str_contain_pair_bool_map_t : public obj_pair_map<expr, expr, expr*> {};
|
||||
|
||||
template<typename Ctx>
|
||||
class binary_search_trail : public trail {
|
||||
obj_map<expr, ptr_vector<expr> > & target;
|
||||
expr * entry;
|
||||
public:
|
||||
binary_search_trail(obj_map<expr, ptr_vector<expr> > & target, expr * entry) :
|
||||
target(target), entry(entry) {}
|
||||
void undo() override {
|
||||
TRACE(t_str_binary_search, tout << "in binary_search_trail::undo()" << std::endl;);
|
||||
if (target.contains(entry)) {
|
||||
if (!target[entry].empty()) {
|
||||
target[entry].pop_back();
|
||||
} else {
|
||||
TRACE(t_str_binary_search, tout << "WARNING: attempt to remove length tester from an empty stack" << std::endl;);
|
||||
}
|
||||
} else {
|
||||
TRACE(t_str_binary_search, tout << "WARNING: attempt to access length tester map via invalid key" << std::endl;);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class regex_automaton_under_assumptions {
|
||||
protected:
|
||||
expr * re_term;
|
||||
eautomaton * aut;
|
||||
bool polarity;
|
||||
|
||||
bool assume_lower_bound;
|
||||
rational lower_bound;
|
||||
|
||||
bool assume_upper_bound;
|
||||
rational upper_bound;
|
||||
public:
|
||||
regex_automaton_under_assumptions() :
|
||||
re_term(nullptr), aut(nullptr), polarity(false),
|
||||
assume_lower_bound(false), assume_upper_bound(false) {}
|
||||
|
||||
regex_automaton_under_assumptions(expr * re_term, eautomaton * aut, bool polarity) :
|
||||
re_term(re_term), aut(aut), polarity(polarity),
|
||||
assume_lower_bound(false), assume_upper_bound(false) {}
|
||||
|
||||
void set_lower_bound(rational & lb) {
|
||||
lower_bound = lb;
|
||||
assume_lower_bound = true;
|
||||
}
|
||||
void unset_lower_bound() {
|
||||
assume_lower_bound = false;
|
||||
}
|
||||
|
||||
void set_upper_bound(rational & ub) {
|
||||
upper_bound = ub;
|
||||
assume_upper_bound = true;
|
||||
}
|
||||
void unset_upper_bound() {
|
||||
assume_upper_bound = false;
|
||||
}
|
||||
|
||||
bool get_lower_bound(rational & lb) const {
|
||||
if (assume_lower_bound) {
|
||||
lb = lower_bound;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool get_upper_bound(rational & ub) const {
|
||||
if (assume_upper_bound) {
|
||||
ub = upper_bound;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
eautomaton * get_automaton() const { return aut; }
|
||||
expr * get_regex_term() const { return re_term; }
|
||||
bool get_polarity() const { return polarity; }
|
||||
};
|
||||
|
||||
class char_union_find {
|
||||
unsigned_vector m_find;
|
||||
unsigned_vector m_size;
|
||||
unsigned_vector m_next;
|
||||
|
||||
integer_set char_const_set;
|
||||
|
||||
u_map<svector<expr*> > m_justification; // representative -> list of formulas justifying EQC
|
||||
|
||||
void ensure_size(unsigned v) {
|
||||
while (v >= get_num_vars()) {
|
||||
mk_var();
|
||||
}
|
||||
}
|
||||
public:
|
||||
unsigned mk_var() {
|
||||
unsigned r = m_find.size();
|
||||
m_find.push_back(r);
|
||||
m_size.push_back(1);
|
||||
m_next.push_back(r);
|
||||
return r;
|
||||
}
|
||||
unsigned get_num_vars() const { return m_find.size(); }
|
||||
void mark_as_char_const(unsigned r) {
|
||||
char_const_set.insert((int)r);
|
||||
}
|
||||
bool is_char_const(unsigned r) {
|
||||
return char_const_set.contains((int)r);
|
||||
}
|
||||
|
||||
unsigned find(unsigned v) const {
|
||||
if (v >= get_num_vars()) {
|
||||
return v;
|
||||
}
|
||||
while (true) {
|
||||
unsigned new_v = m_find[v];
|
||||
if (new_v == v)
|
||||
return v;
|
||||
v = new_v;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned next(unsigned v) const {
|
||||
if (v >= get_num_vars()) {
|
||||
return v;
|
||||
}
|
||||
return m_next[v];
|
||||
}
|
||||
|
||||
bool is_root(unsigned v) const {
|
||||
return v >= get_num_vars() || m_find[v] == v;
|
||||
}
|
||||
|
||||
svector<expr*> get_justification(unsigned v) {
|
||||
unsigned r = find(v);
|
||||
svector<expr*> retval;
|
||||
if (m_justification.find(r, retval)) {
|
||||
return retval;
|
||||
} else {
|
||||
return svector<expr*>();
|
||||
}
|
||||
}
|
||||
|
||||
void merge(unsigned v1, unsigned v2, expr * justification) {
|
||||
unsigned r1 = find(v1);
|
||||
unsigned r2 = find(v2);
|
||||
if (r1 == r2)
|
||||
return;
|
||||
ensure_size(v1);
|
||||
ensure_size(v2);
|
||||
// swap r1 and r2 if:
|
||||
// 1. EQC of r1 is bigger than EQC of r2
|
||||
// 2. r1 is a character constant and r2 is not.
|
||||
// this maintains the invariant that if a character constant is in an eqc then it is the root of that eqc
|
||||
if (m_size[r1] > m_size[r2] || (is_char_const(r1) && !is_char_const(r2))) {
|
||||
std::swap(r1, r2);
|
||||
}
|
||||
m_find[r1] = r2;
|
||||
m_size[r2] += m_size[r1];
|
||||
std::swap(m_next[r1], m_next[r2]);
|
||||
|
||||
if (m_justification.contains(r1)) {
|
||||
// add r1's justifications to r2
|
||||
if (!m_justification.contains(r2)) {
|
||||
m_justification.insert(r2, m_justification[r1]);
|
||||
} else {
|
||||
m_justification[r2].append(m_justification[r1]);
|
||||
}
|
||||
m_justification.remove(r1);
|
||||
}
|
||||
if (justification != nullptr) {
|
||||
if (!m_justification.contains(r2)) {
|
||||
m_justification.insert(r2, svector<expr*>());
|
||||
}
|
||||
m_justification[r2].push_back(justification);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_find.reset();
|
||||
m_next.reset();
|
||||
m_size.reset();
|
||||
char_const_set.reset();
|
||||
m_justification.reset();
|
||||
}
|
||||
};
|
||||
|
||||
class theory_str : public theory {
|
||||
struct T_cut
|
||||
{
|
||||
int level;
|
||||
obj_map<expr, int> vars;
|
||||
|
||||
T_cut() {
|
||||
level = -100;
|
||||
}
|
||||
};
|
||||
|
||||
typedef union_find<theory_str> th_union_find;
|
||||
|
||||
typedef map<rational, expr*, obj_hash<rational>, default_eq<rational> > rational_map;
|
||||
struct zstring_hash_proc {
|
||||
unsigned operator()(zstring const & s) const {
|
||||
auto str = s.encode();
|
||||
return string_hash(str.c_str(), static_cast<unsigned>(s.length()), 17);
|
||||
}
|
||||
};
|
||||
typedef map<zstring, expr*, zstring_hash_proc, default_eq<zstring> > string_map;
|
||||
|
||||
struct stats {
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(stats)); }
|
||||
unsigned m_refine_eq;
|
||||
unsigned m_refine_neq;
|
||||
unsigned m_refine_f;
|
||||
unsigned m_refine_nf;
|
||||
unsigned m_solved_by;
|
||||
unsigned m_fixed_length_iterations;
|
||||
};
|
||||
|
||||
protected:
|
||||
theory_str_params const & m_params;
|
||||
|
||||
/*
|
||||
* Setting EagerStringConstantLengthAssertions to true allows some methods,
|
||||
* in particular internalize_term(), to add
|
||||
* length assertions about relevant string constants.
|
||||
* Note that currently this should always be set to 'true', or else *no* length assertions
|
||||
* will be made about string constants.
|
||||
*/
|
||||
bool opt_EagerStringConstantLengthAssertions;
|
||||
|
||||
/*
|
||||
* If VerifyFinalCheckProgress is set to true, continuing after final check is invoked
|
||||
* without asserting any new axioms is considered a bug and will throw an exception.
|
||||
*/
|
||||
bool opt_VerifyFinalCheckProgress;
|
||||
|
||||
/*
|
||||
* This constant controls how eagerly we expand unrolls in unbounded regex membership tests.
|
||||
*/
|
||||
int opt_LCMUnrollStep;
|
||||
|
||||
/*
|
||||
* If NoQuickReturn_IntegerTheory is set to true,
|
||||
* integer theory integration checks that assert axioms
|
||||
* will not return from the function after asserting their axioms.
|
||||
* The default behaviour of Z3str2 is to set this to 'false'. This may be incorrect.
|
||||
*/
|
||||
bool opt_NoQuickReturn_IntegerTheory;
|
||||
|
||||
/*
|
||||
* If DisableIntegerTheoryIntegration is set to true,
|
||||
* ALL calls to the integer theory integration methods
|
||||
* (get_arith_value, get_len_value, lower_bound, upper_bound)
|
||||
* will ignore what the arithmetic solver believes about length terms,
|
||||
* and will return no information.
|
||||
*
|
||||
* This reduces performance significantly, but can be useful to enable
|
||||
* if it is suspected that string-integer integration, or the arithmetic solver itself,
|
||||
* might have a bug.
|
||||
*
|
||||
* The default behaviour of Z3str2 is to set this to 'false'.
|
||||
*/
|
||||
bool opt_DisableIntegerTheoryIntegration;
|
||||
|
||||
/*
|
||||
* If DeferEQCConsistencyCheck is set to true,
|
||||
* expensive calls to new_eq_check() will be deferred until final check,
|
||||
* at which time the consistency of *all* string equivalence classes will be validated.
|
||||
*/
|
||||
bool opt_DeferEQCConsistencyCheck;
|
||||
|
||||
/*
|
||||
* If CheckVariableScope is set to true,
|
||||
* pop_scope_eh() and final_check_eh() will run extra checks
|
||||
* to determine whether the current assignment
|
||||
* contains references to any internal variables that are no longer in scope.
|
||||
*/
|
||||
bool opt_CheckVariableScope;
|
||||
|
||||
/*
|
||||
* If ConcatOverlapAvoid is set to true,
|
||||
* the check to simplify Concat = Concat in handle_equality() will
|
||||
* avoid simplifying wrt. pairs of Concat terms that will immediately
|
||||
* result in an overlap. (false = Z3str2 behaviour)
|
||||
*/
|
||||
bool opt_ConcatOverlapAvoid;
|
||||
|
||||
bool search_started;
|
||||
arith_util m_autil;
|
||||
seq_util u;
|
||||
int sLevel;
|
||||
|
||||
bool finalCheckProgressIndicator;
|
||||
|
||||
expr_ref_vector m_trail; // trail for generated terms
|
||||
|
||||
str_value_factory * m_factory;
|
||||
|
||||
re2automaton m_mk_aut;
|
||||
|
||||
// Unique identifier appended to unused variables to ensure that model construction
|
||||
// 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;
|
||||
|
||||
ptr_vector<enode> m_basicstr_axiom_todo;
|
||||
ptr_vector<enode> m_concat_axiom_todo;
|
||||
ptr_vector<enode> m_string_constant_length_todo;
|
||||
ptr_vector<enode> m_concat_eval_todo;
|
||||
expr_ref_vector m_delayed_assertions_todo;
|
||||
|
||||
// enode lists for library-aware/high-level string terms (e.g. substr, contains)
|
||||
ptr_vector<enode> m_library_aware_axiom_todo;
|
||||
|
||||
// list of axioms that are re-asserted every time the scope is popped
|
||||
expr_ref_vector m_persisted_axioms;
|
||||
expr_ref_vector m_persisted_axiom_todo;
|
||||
|
||||
// hashtable of all exprs for which we've already set up term-specific axioms --
|
||||
// this prevents infinite recursive descent with respect to axioms that
|
||||
// include an occurrence of the term for which axioms are being generated
|
||||
obj_hashtable<expr> axiomatized_terms;
|
||||
|
||||
// hashtable of all top-level exprs for which set_up_axioms() has been called
|
||||
obj_hashtable<expr> existing_toplevel_exprs;
|
||||
|
||||
int tmpStringVarCount;
|
||||
int tmpXorVarCount;
|
||||
// obj_pair_map<expr, expr, std::map<int, expr*> > varForBreakConcat;
|
||||
std::map<std::pair<expr*,expr*>, std::map<int, expr*> > varForBreakConcat;
|
||||
bool avoidLoopCut;
|
||||
bool loopDetected;
|
||||
obj_map<expr, std::stack<T_cut*> > cut_var_map;
|
||||
scoped_ptr_vector<T_cut> m_cut_allocs;
|
||||
expr_ref m_theoryStrOverlapAssumption_term;
|
||||
|
||||
obj_hashtable<expr> variable_set;
|
||||
obj_hashtable<expr> internal_variable_set;
|
||||
std::map<int, obj_hashtable<expr> > internal_variable_scope_levels;
|
||||
|
||||
expr_ref_vector contains_map;
|
||||
|
||||
theory_str_contain_pair_bool_map_t contain_pair_bool_map;
|
||||
obj_map<expr, std::set<std::pair<expr*, expr*> > > contain_pair_idx_map;
|
||||
|
||||
// regex automata
|
||||
scoped_ptr_vector<eautomaton> m_automata;
|
||||
ptr_vector<eautomaton> regex_automata;
|
||||
obj_hashtable<expr> regex_terms;
|
||||
obj_map<expr, ptr_vector<expr> > regex_terms_by_string; // S --> [ (str.in.re S *) ]
|
||||
obj_map<expr, svector<regex_automaton_under_assumptions> > regex_automaton_assumptions; // RegEx --> [ aut+assumptions ]
|
||||
obj_hashtable<expr> regex_terms_with_path_constraints; // set of string terms which have had path constraints asserted in the current scope
|
||||
obj_hashtable<expr> regex_terms_with_length_constraints; // set of regex terms which had had length constraints asserted in the current scope
|
||||
obj_map<expr, expr*> regex_term_to_length_constraint; // (str.in.re S R) -> (length constraint over S wrt. R)
|
||||
obj_map<expr, ptr_vector<expr> > regex_term_to_extra_length_vars; // extra length vars used in regex_term_to_length_constraint entries
|
||||
|
||||
// keep track of the last lower/upper bound we saw for each string term
|
||||
// so we don't perform duplicate work
|
||||
obj_map<expr, rational> regex_last_lower_bound;
|
||||
obj_map<expr, rational> regex_last_upper_bound;
|
||||
|
||||
// each counter maps a (str.in.re) expression to an integer.
|
||||
// use helper functions regex_inc_counter() and regex_get_counter() to access
|
||||
obj_map<expr, unsigned> regex_length_attempt_count;
|
||||
obj_map<expr, unsigned> regex_fail_count;
|
||||
obj_map<expr, unsigned> regex_intersection_fail_count;
|
||||
|
||||
obj_map<expr, ptr_vector<expr> > string_chars; // S --> [S_0, S_1, ...] for character terms S_i
|
||||
|
||||
obj_pair_map<expr, expr, expr*> concat_astNode_map;
|
||||
|
||||
// all (str.to-int) and (int.to-str) terms
|
||||
expr_ref_vector string_int_conversion_terms;
|
||||
obj_hashtable<expr> string_int_axioms;
|
||||
|
||||
string_map stringConstantCache;
|
||||
unsigned long totalCacheAccessCount;
|
||||
unsigned long cacheHitCount;
|
||||
unsigned long cacheMissCount;
|
||||
|
||||
unsigned m_fresh_id;
|
||||
|
||||
// cache mapping each string S to Length(S)
|
||||
obj_map<expr, app*> length_ast_map;
|
||||
|
||||
trail_stack m_trail_stack;
|
||||
trail_stack m_library_aware_trail_stack;
|
||||
th_union_find m_find;
|
||||
theory_var get_var(expr * n) const;
|
||||
expr * get_eqc_next(expr * n);
|
||||
app * get_ast(theory_var i);
|
||||
|
||||
// fixed length model construction
|
||||
expr_ref_vector fixed_length_subterm_trail; // trail for subterms generated *in the subsolver*
|
||||
expr_ref_vector fixed_length_assumptions; // cache of boolean terms to assert *into the subsolver*, unsat core is a subset of these
|
||||
obj_map<expr, rational> fixed_length_used_len_terms; // constraints used in generating fixed length model
|
||||
obj_map<expr, expr_ref_vector* > var_to_char_subterm_map; // maps a var to a list of character terms *in the subsolver*
|
||||
obj_map<expr, expr_ref_vector* > uninterpreted_to_char_subterm_map; // maps an "uninterpreted" string term to a list of character terms *in the subsolver*
|
||||
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:
|
||||
void reset_internal_data_structures();
|
||||
|
||||
void assert_axiom(expr * e);
|
||||
void assert_implication(expr * premise, expr * conclusion);
|
||||
expr * rewrite_implication(expr * premise, expr * conclusion);
|
||||
// Use the rewriter to simplify an axiom, then assert it.
|
||||
void assert_axiom_rw(expr * e);
|
||||
|
||||
expr * mk_string(zstring const& str);
|
||||
expr * mk_string(const char * str);
|
||||
|
||||
app * mk_strlen(expr * e);
|
||||
expr * mk_concat(expr * n1, expr * n2);
|
||||
expr * mk_concat_const_str(expr * n1, expr * n2);
|
||||
app * mk_contains(expr * haystack, expr * needle);
|
||||
app * mk_indexof(expr * haystack, expr * needle);
|
||||
app * mk_fresh_const(char const* name, sort* s);
|
||||
|
||||
literal mk_literal(expr* _e);
|
||||
app * mk_int(int n);
|
||||
app * mk_int(rational & q);
|
||||
|
||||
void check_and_init_cut_var(expr * node);
|
||||
void add_cut_info_one_node(expr * baseNode, int slevel, expr * node);
|
||||
void add_cut_info_merge(expr * destNode, int slevel, expr * srcNode);
|
||||
bool has_self_cut(expr * n1, expr * n2);
|
||||
|
||||
// for ConcatOverlapAvoid
|
||||
bool will_result_in_overlap(expr * lhs, expr * rhs);
|
||||
|
||||
void track_variable_scope(expr * var);
|
||||
app * mk_str_var(std::string name);
|
||||
app * mk_int_var(std::string name);
|
||||
app_ref mk_nonempty_str_var();
|
||||
app * mk_internal_xor_var();
|
||||
void add_nonempty_constraint(expr * s);
|
||||
|
||||
void instantiate_concat_axiom(enode * cat);
|
||||
void try_eval_concat(enode * cat);
|
||||
void instantiate_basic_string_axioms(enode * str);
|
||||
void instantiate_str_eq_length_axiom(enode * lhs, enode * rhs);
|
||||
|
||||
// for count abstraction and refinement
|
||||
expr* refine(expr* lhs, expr* rhs, rational offset);
|
||||
expr* refine_eq(expr* lhs, expr* rhs, unsigned offset);
|
||||
expr* refine_dis(expr* lhs, expr* rhs);
|
||||
expr* refine_function(expr* f);
|
||||
bool flatten(expr* ex, expr_ref_vector & flat);
|
||||
rational get_refine_length(expr* ex, expr_ref_vector& extra_deps);
|
||||
|
||||
void instantiate_axiom_CharAt(enode * e);
|
||||
void instantiate_axiom_prefixof(enode * e);
|
||||
void instantiate_axiom_suffixof(enode * e);
|
||||
void instantiate_axiom_Contains(enode * e);
|
||||
void instantiate_axiom_Indexof(enode * e);
|
||||
void instantiate_axiom_Indexof_extended(enode * e);
|
||||
void instantiate_axiom_LastIndexof(enode * e);
|
||||
void instantiate_axiom_Substr(enode * e);
|
||||
void instantiate_axiom_Replace(enode * e);
|
||||
void instantiate_axiom_str_to_int(enode * e);
|
||||
void instantiate_axiom_int_to_str(enode * e);
|
||||
void instantiate_axiom_is_digit(enode * e);
|
||||
void instantiate_axiom_str_to_code(enode * e);
|
||||
void instantiate_axiom_str_from_code(enode * e);
|
||||
|
||||
void add_persisted_axiom(expr * a);
|
||||
|
||||
expr * mk_RegexIn(expr * str, expr * regexp);
|
||||
void instantiate_axiom_RegexIn(enode * e);
|
||||
|
||||
// regex automata and length-aware regex
|
||||
bool solve_regex_automata();
|
||||
unsigned estimate_regex_complexity(expr * re);
|
||||
unsigned estimate_regex_complexity_under_complement(expr * re);
|
||||
unsigned estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2);
|
||||
bool check_regex_length_linearity(expr * re);
|
||||
bool check_regex_length_linearity_helper(expr * re, bool already_star);
|
||||
expr_ref infer_all_regex_lengths(expr * lenVar, expr * re, expr_ref_vector & freeVariables);
|
||||
void check_subterm_lengths(expr * re, integer_set & lens);
|
||||
void find_automaton_initial_bounds(expr * str_in_re, eautomaton * aut);
|
||||
bool refine_automaton_lower_bound(eautomaton * aut, rational current_lower_bound, rational & refined_lower_bound);
|
||||
bool refine_automaton_upper_bound(eautomaton * aut, rational current_upper_bound, rational & refined_upper_bound);
|
||||
expr_ref generate_regex_path_constraints(expr * stringTerm, eautomaton * aut, rational lenVal, expr_ref & characterConstraints);
|
||||
void aut_path_add_next(u_map<expr*>& next, expr_ref_vector& trail, unsigned idx, expr* cond);
|
||||
expr_ref aut_path_rewrite_constraint(expr * cond, expr * ch_var);
|
||||
void regex_inc_counter(obj_map<expr, unsigned> & counter_map, expr * key);
|
||||
unsigned regex_get_counter(obj_map<expr, unsigned> & counter_map, expr * key);
|
||||
|
||||
void set_up_axioms(expr * ex);
|
||||
void handle_equality(expr * lhs, expr * rhs);
|
||||
|
||||
app * mk_value_helper(app * n);
|
||||
expr * get_eqc_value(expr * n, bool & hasEqcValue);
|
||||
bool get_string_constant_eqc(expr * n, zstring & stringVal);
|
||||
expr * z3str2_get_eqc_value(expr * n , bool & hasEqcValue);
|
||||
bool in_same_eqc(expr * n1, expr * n2);
|
||||
expr * collect_eq_nodes(expr * n, expr_ref_vector & eqcSet);
|
||||
bool is_var(expr * e) const;
|
||||
|
||||
bool get_arith_value(expr* e, rational& val) const;
|
||||
bool get_len_value(expr* e, rational& val);
|
||||
bool lower_bound(expr* _e, rational& lo);
|
||||
bool upper_bound(expr* _e, rational& hi);
|
||||
|
||||
bool can_two_nodes_eq(expr * n1, expr * n2);
|
||||
bool can_concat_eq_str(expr * concat, zstring& str);
|
||||
bool can_concat_eq_concat(expr * concat1, expr * concat2);
|
||||
bool check_concat_len_in_eqc(expr * concat);
|
||||
void check_eqc_empty_string(expr * lhs, expr * rhs);
|
||||
void check_eqc_concat_concat(std::set<expr*> & eqc_concat_lhs, std::set<expr*> & eqc_concat_rhs);
|
||||
bool check_length_consistency(expr * n1, expr * n2);
|
||||
bool check_length_const_string(expr * n1, expr * constStr);
|
||||
bool check_length_eq_var_concat(expr * n1, expr * n2);
|
||||
bool check_length_concat_concat(expr * n1, expr * n2);
|
||||
bool check_length_concat_var(expr * concat, expr * var);
|
||||
bool check_length_var_var(expr * var1, expr * var2);
|
||||
void check_contain_in_new_eq(expr * n1, expr * n2);
|
||||
void check_contain_by_eqc_val(expr * varNode, expr * constNode);
|
||||
void check_contain_by_substr(expr * varNode, expr_ref_vector & willEqClass);
|
||||
void check_contain_by_eq_nodes(expr * n1, expr * n2);
|
||||
bool in_contain_idx_map(expr * n);
|
||||
void compute_contains(std::map<expr*, expr*> & varAliasMap,
|
||||
std::map<expr*, expr*> & concatAliasMap, std::map<expr*, expr *> & varConstMap,
|
||||
std::map<expr*, expr*> & concatConstMap, std::map<expr*, std::map<expr*, int> > & varEqConcatMap);
|
||||
expr * dealias_node(expr * node, std::map<expr*, expr*> & varAliasMap, std::map<expr*, expr*> & concatAliasMap);
|
||||
void get_grounded_concats(unsigned depth,
|
||||
expr* node, std::map<expr*, expr*> & varAliasMap,
|
||||
std::map<expr*, expr*> & concatAliasMap, std::map<expr*, expr*> & varConstMap,
|
||||
std::map<expr*, expr*> & concatConstMap, std::map<expr*, std::map<expr*, int> > & varEqConcatMap,
|
||||
std::map<expr*, std::map<std::vector<expr*>, std::set<expr*> > > & groundedMap);
|
||||
void print_grounded_concat(expr * node, std::map<expr*, std::map<std::vector<expr*>, std::set<expr*> > > & groundedMap);
|
||||
void check_subsequence(expr* str, expr* strDeAlias, expr* subStr, expr* subStrDeAlias, expr* boolVar,
|
||||
std::map<expr*, std::map<std::vector<expr*>, std::set<expr*> > > & groundedMap);
|
||||
bool is_partial_in_grounded_concat(const std::vector<expr*> & strVec, const std::vector<expr*> & subStrVec);
|
||||
|
||||
void get_nodes_in_concat(expr * node, ptr_vector<expr> & nodeList);
|
||||
expr * simplify_concat(expr * node);
|
||||
|
||||
void simplify_parent(expr * nn, expr * eq_str);
|
||||
|
||||
void simplify_concat_equality(expr * lhs, expr * rhs);
|
||||
void solve_concat_eq_str(expr * concat, expr * str);
|
||||
|
||||
void infer_len_concat_equality(expr * nn1, expr * nn2);
|
||||
bool infer_len_concat(expr * n, rational & nLen);
|
||||
void infer_len_concat_arg(expr * n, rational len);
|
||||
|
||||
bool is_concat_eq_type1(expr * concatAst1, expr * concatAst2);
|
||||
bool is_concat_eq_type2(expr * concatAst1, expr * concatAst2);
|
||||
bool is_concat_eq_type3(expr * concatAst1, expr * concatAst2);
|
||||
bool is_concat_eq_type4(expr * concatAst1, expr * concatAst2);
|
||||
bool is_concat_eq_type5(expr * concatAst1, expr * concatAst2);
|
||||
bool is_concat_eq_type6(expr * concatAst1, expr * concatAst2);
|
||||
|
||||
void process_concat_eq_type1(expr * concatAst1, expr * concatAst2);
|
||||
void process_concat_eq_type2(expr * concatAst1, expr * concatAst2);
|
||||
void process_concat_eq_type3(expr * concatAst1, expr * concatAst2);
|
||||
void process_concat_eq_type4(expr * concatAst1, expr * concatAst2);
|
||||
void process_concat_eq_type5(expr * concatAst1, expr * concatAst2);
|
||||
void process_concat_eq_type6(expr * concatAst1, expr * concatAst2);
|
||||
|
||||
void print_cut_var(expr * node, std::ofstream & xout);
|
||||
|
||||
void generate_mutual_exclusion(expr_ref_vector & exprs);
|
||||
void add_theory_aware_branching_info(expr * term, double priority, lbool phase);
|
||||
|
||||
bool new_eq_check(expr * lhs, expr * rhs);
|
||||
void group_terms_by_eqc(expr * n, std::set<expr*> & concats, std::set<expr*> & vars, std::set<expr*> & consts);
|
||||
|
||||
void check_consistency_prefix(expr * e, bool is_true);
|
||||
void check_consistency_suffix(expr * e, bool is_true);
|
||||
void check_consistency_contains(expr * e, bool is_true);
|
||||
|
||||
int ctx_dep_analysis(std::map<expr*, int> & strVarMap, std::map<expr*, int> & freeVarMap,
|
||||
std::map<expr*, std::map<expr*, int> > & var_eq_concat_map);
|
||||
void trace_ctx_dep(std::ofstream & tout,
|
||||
std::map<expr*, expr*> & aliasIndexMap,
|
||||
std::map<expr*, expr*> & var_eq_constStr_map,
|
||||
std::map<expr*, std::map<expr*, int> > & var_eq_concat_map,
|
||||
std::map<expr*, std::map<expr*, int> > & var_eq_unroll_map,
|
||||
std::map<expr*, expr*> & concat_eq_constStr_map,
|
||||
std::map<expr*, std::map<expr*, int> > & concat_eq_concat_map);
|
||||
|
||||
bool term_appears_as_subterm(expr * needle, expr * haystack);
|
||||
void classify_ast_by_type(expr * node, std::map<expr*, int> & varMap,
|
||||
std::map<expr*, int> & concatMap, std::map<expr*, int> & unrollMap);
|
||||
void classify_ast_by_type_in_positive_context(std::map<expr*, int> & varMap,
|
||||
std::map<expr*, int> & concatMap, std::map<expr*, int> & unrollMap);
|
||||
|
||||
expr * get_alias_index_ast(std::map<expr*, expr*> & aliasIndexMap, expr * node);
|
||||
expr * getMostLeftNodeInConcat(expr * node);
|
||||
expr * getMostRightNodeInConcat(expr * node);
|
||||
void get_var_in_eqc(expr * n, std::set<expr*> & varSet);
|
||||
void get_concats_in_eqc(expr * n, std::set<expr*> & concats);
|
||||
void get_const_str_asts_in_node(expr * node, expr_ref_vector & constList);
|
||||
expr * eval_concat(expr * n1, expr * n2);
|
||||
|
||||
bool finalcheck_str2int(app * a);
|
||||
bool finalcheck_int2str(app * a);
|
||||
bool string_integer_conversion_valid(zstring str, rational& converted) const;
|
||||
|
||||
lbool fixed_length_model_construction(expr_ref_vector formulas, expr_ref_vector &precondition,
|
||||
expr_ref_vector& free_variables,
|
||||
obj_map<expr, zstring> &model, expr_ref_vector &cex);
|
||||
bool fixed_length_reduce_string_term(smt::kernel & subsolver, expr * term, expr_ref_vector & term_chars, expr_ref & cex);
|
||||
bool fixed_length_get_len_value(expr * e, rational & val);
|
||||
bool fixed_length_reduce_eq(smt::kernel & subsolver, expr_ref lhs, expr_ref rhs, expr_ref & cex);
|
||||
bool fixed_length_reduce_diseq(smt::kernel & subsolver, expr_ref lhs, expr_ref rhs, expr_ref & cex);
|
||||
bool fixed_length_reduce_contains(smt::kernel & subsolver, expr_ref f, expr_ref & cex);
|
||||
bool fixed_length_reduce_negative_contains(smt::kernel & subsolver, expr_ref f, expr_ref & cex);
|
||||
bool fixed_length_reduce_prefix(smt::kernel & subsolver, expr_ref f, expr_ref & cex);
|
||||
bool fixed_length_reduce_negative_prefix(smt::kernel & subsolver, expr_ref f, expr_ref & cex);
|
||||
bool fixed_length_reduce_suffix(smt::kernel & subsolver, expr_ref f, expr_ref & cex);
|
||||
bool fixed_length_reduce_negative_suffix(smt::kernel & subsolver, expr_ref f, expr_ref & cex);
|
||||
bool fixed_length_reduce_regex_membership(smt::kernel & subsolver, expr_ref f, expr_ref & cex, bool polarity);
|
||||
|
||||
void dump_assignments();
|
||||
|
||||
void check_variable_scope();
|
||||
void recursive_check_variable_scope(expr * ex);
|
||||
|
||||
void collect_var_concat(expr * node, std::set<expr*> & varSet, std::set<expr*> & concatSet);
|
||||
bool propagate_length(std::set<expr*> & varSet, std::set<expr*> & concatSet, std::map<expr*, int> & exprLenMap);
|
||||
void get_unique_non_concat_nodes(expr * node, std::set<expr*> & argSet);
|
||||
bool propagate_length_within_eqc(expr * var);
|
||||
|
||||
|
||||
const rational NEQ = rational(-1); // negative word equation lesson
|
||||
const rational PFUN = rational(-2); // positive function lesson
|
||||
const rational NFUN = rational(-3); // negative function lesson
|
||||
|
||||
// TESTING
|
||||
void refresh_theory_var(expr * e);
|
||||
|
||||
public:
|
||||
theory_str(context& ctx, ast_manager & m, theory_str_params const & params);
|
||||
~theory_str() override;
|
||||
|
||||
char const * get_name() const override { return "seq"; }
|
||||
void init() override;
|
||||
void display(std::ostream & out) const override;
|
||||
|
||||
void collect_statistics(::statistics & st) const override;
|
||||
|
||||
bool overlapping_variables_detected() const { return loopDetected; }
|
||||
|
||||
trail_stack& get_trail_stack() { return m_trail_stack; }
|
||||
void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2) {}
|
||||
void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { }
|
||||
void unmerge_eh(theory_var v1, theory_var v2) {}
|
||||
protected:
|
||||
bool internalize_atom(app * atom, bool gate_ctx) override;
|
||||
bool internalize_term(app * term) override;
|
||||
virtual enode* ensure_enode(expr* e);
|
||||
theory_var mk_var(enode * n) override;
|
||||
|
||||
void new_eq_eh(theory_var, theory_var) override;
|
||||
void new_diseq_eh(theory_var, theory_var) override;
|
||||
|
||||
theory* mk_fresh(context* c) override { return alloc(theory_str, *c, c->get_manager(), m_params); }
|
||||
void init_search_eh() override;
|
||||
void add_theory_assumptions(expr_ref_vector & assumptions) override;
|
||||
lbool validate_unsat_core(expr_ref_vector & unsat_core) override;
|
||||
void relevant_eh(app * n) override;
|
||||
void assign_eh(bool_var v, bool is_true) override;
|
||||
void push_scope_eh() override;
|
||||
void pop_scope_eh(unsigned num_scopes) override;
|
||||
void reset_eh() override;
|
||||
|
||||
bool can_propagate() override;
|
||||
void propagate() override;
|
||||
|
||||
final_check_status final_check_eh() override;
|
||||
virtual void attach_new_th_var(enode * n);
|
||||
|
||||
void init_model(model_generator & m) override;
|
||||
model_value_proc * mk_value(enode * n, model_generator & mg) override;
|
||||
void finalize_model(model_generator & mg) override;
|
||||
};
|
||||
|
||||
};
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -110,6 +110,15 @@ void theory_user_propagator::register_cb(expr* e) {
|
|||
add_expr(e, true);
|
||||
}
|
||||
|
||||
void theory_user_propagator::register_on_binding(user_propagator::binding_eh_t& binding_eh) {
|
||||
std::function<bool(quantifier* q, expr* inst)> on_binding =
|
||||
[this, binding_eh](quantifier* q, expr* inst) {
|
||||
return binding_eh(m_user_context, this, q, inst);
|
||||
};
|
||||
ctx.register_on_binding(on_binding);
|
||||
|
||||
}
|
||||
|
||||
bool theory_user_propagator::next_split_cb(expr* e, unsigned idx, lbool phase) {
|
||||
if (e == nullptr) { // clear
|
||||
m_next_split_var = nullptr;
|
||||
|
|
|
@ -132,6 +132,7 @@ namespace smt {
|
|||
void register_diseq(user_propagator::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; }
|
||||
void register_created(user_propagator::created_eh_t& created_eh) { m_created_eh = created_eh; }
|
||||
void register_decide(user_propagator::decide_eh_t& decide_eh) { m_decide_eh = decide_eh; }
|
||||
void register_on_binding(user_propagator::binding_eh_t& binding_eh);
|
||||
|
||||
bool has_fixed() const { return (bool)m_fixed_eh; }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue