mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +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
				
			
		|  | @ -46,7 +46,6 @@ z3_add_component(rewriter | |||
|   COMPONENT_DEPENDENCIES | ||||
|     ast | ||||
|     params | ||||
|     automata | ||||
|     interval | ||||
|     polynomial | ||||
| ) | ||||
|  |  | |||
|  | @ -29,8 +29,6 @@ Authors: | |||
| #include "ast/rewriter/var_subst.h" | ||||
| #include "ast/rewriter/expr_safe_replace.h" | ||||
| #include "params/seq_rewriter_params.hpp" | ||||
| #include "math/automata/automaton.h" | ||||
| #include "math/automata/symbolic_automata_def.h" | ||||
| 
 | ||||
| 
 | ||||
| expr_ref sym_expr::accept(expr* e) { | ||||
|  | @ -83,320 +81,6 @@ struct display_expr1 { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| class sym_expr_boolean_algebra : public boolean_algebra<sym_expr*> { | ||||
|     ast_manager& m; | ||||
|     expr_solver& m_solver; | ||||
|     expr_ref     m_var; | ||||
|     typedef sym_expr* T; | ||||
| public: | ||||
|     sym_expr_boolean_algebra(ast_manager& m, expr_solver& s):  | ||||
|         m(m), m_solver(s), m_var(m) {} | ||||
| 
 | ||||
|     T mk_false() override { | ||||
|         expr_ref fml(m.mk_false(), m); | ||||
|         return sym_expr::mk_pred(fml, m.mk_bool_sort()); // use of Bool sort for bound variable is arbitrary
 | ||||
|     } | ||||
|     T mk_true() override { | ||||
|         expr_ref fml(m.mk_true(), m); | ||||
|         return sym_expr::mk_pred(fml, m.mk_bool_sort()); | ||||
|     } | ||||
|     T mk_and(T x, T y) override { | ||||
|         seq_util u(m); | ||||
|         if (x->is_char() && y->is_char()) { | ||||
|             if (x->get_char() == y->get_char()) { | ||||
|                 return x; | ||||
|             } | ||||
|             if (m.are_distinct(x->get_char(), y->get_char())) { | ||||
|                 expr_ref fml(m.mk_false(), m); | ||||
|                 return sym_expr::mk_pred(fml, x->get_sort()); | ||||
|             } | ||||
|         } | ||||
|         unsigned lo1, hi1, lo2, hi2; | ||||
|         if (x->is_range() && y->is_range() && | ||||
|             u.is_const_char(x->get_lo(), lo1) && u.is_const_char(x->get_hi(), hi1) && | ||||
|             u.is_const_char(y->get_lo(), lo2) && u.is_const_char(y->get_hi(), hi2)) { | ||||
|             lo1 = std::max(lo1, lo2); | ||||
|             hi1 = std::min(hi1, hi2); | ||||
|             if (lo1 > hi1) { | ||||
|                 expr_ref fml(m.mk_false(), m); | ||||
|                 return sym_expr::mk_pred(fml, x->get_sort()); | ||||
|             } | ||||
|             expr_ref _start(u.mk_char(lo1), m); | ||||
|             expr_ref _stop(u.mk_char(hi1), m); | ||||
|             return sym_expr::mk_range(_start, _stop); | ||||
|         } | ||||
| 
 | ||||
|         sort* s = x->get_sort(); | ||||
|         if (m.is_bool(s)) s = y->get_sort(); | ||||
|         var_ref v(m.mk_var(0, s), m); | ||||
|         expr_ref fml1 = x->accept(v); | ||||
|         expr_ref fml2 = y->accept(v); | ||||
|         if (m.is_true(fml1)) { | ||||
|             return y; | ||||
|         } | ||||
|         if (m.is_true(fml2)) { | ||||
|             return x; | ||||
|         } | ||||
|         if (fml1 == fml2) { | ||||
|             return x;    | ||||
|         } | ||||
|         if (is_complement(fml1, fml2)) { | ||||
|             expr_ref ff(m.mk_false(), m); | ||||
|             return sym_expr::mk_pred(ff, x->get_sort()); | ||||
|         } | ||||
|         expr_ref fml(m); | ||||
|         bool_rewriter br(m); | ||||
|         br.mk_and(fml1, fml2, fml); | ||||
|         return sym_expr::mk_pred(fml, x->get_sort()); | ||||
|     } | ||||
| 
 | ||||
|     bool is_complement(expr* f1, expr* f2) { | ||||
|         expr* f = nullptr; | ||||
|         return  | ||||
|             (m.is_not(f1, f) && f == f2) || | ||||
|             (m.is_not(f2, f) && f == f1); | ||||
|     } | ||||
| 
 | ||||
|     T mk_or(T x, T y) override { | ||||
|         if (x->is_char() && y->is_char() && | ||||
|             x->get_char() == y->get_char()) { | ||||
|             return x; | ||||
|         } | ||||
|         if (x == y) return x; | ||||
|         var_ref v(m.mk_var(0, x->get_sort()), m); | ||||
|         expr_ref fml1 = x->accept(v); | ||||
|         expr_ref fml2 = y->accept(v);         | ||||
|         if (m.is_false(fml1)) return y; | ||||
|         if (m.is_false(fml2)) return x; | ||||
|         bool_rewriter br(m); | ||||
|         expr_ref fml(m); | ||||
|         br.mk_or(fml1, fml2, fml); | ||||
|         return sym_expr::mk_pred(fml, x->get_sort()); | ||||
|     } | ||||
| 
 | ||||
|     T mk_and(unsigned sz, T const* ts) override { | ||||
|         switch (sz) { | ||||
|         case 0: return mk_true(); | ||||
|         case 1: return ts[0]; | ||||
|         default: { | ||||
|             T t = ts[0]; | ||||
|             for (unsigned i = 1; i < sz; ++i) { | ||||
|                 t = mk_and(t, ts[i]); | ||||
|             } | ||||
|             return t; | ||||
|         } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     T mk_or(unsigned sz, T const* ts) override { | ||||
|         switch (sz) { | ||||
|         case 0: return mk_false(); | ||||
|         case 1: return ts[0]; | ||||
|         default: { | ||||
|             T t = ts[0]; | ||||
|             for (unsigned i = 1; i < sz; ++i) { | ||||
|                 t = mk_or(t, ts[i]); | ||||
|             } | ||||
|             return t; | ||||
|         } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     lbool is_sat(T x) override { | ||||
|         unsigned lo, hi; | ||||
|         seq_util u(m); | ||||
| 
 | ||||
|         if (x->is_char()) { | ||||
|             return l_true; | ||||
|         } | ||||
|         if (x->is_range() && u.is_const_char(x->get_lo(), lo) && u.is_const_char(x->get_hi(), hi)) { | ||||
|             return (lo <= hi) ? l_true : l_false;  | ||||
|         } | ||||
|         if (x->is_not() && x->get_arg()->is_range() && u.is_const_char(x->get_arg()->get_lo(), lo) && 0 < lo) { | ||||
|             return l_true; | ||||
|         }             | ||||
|         if (!m_var || m_var->get_sort() != x->get_sort()) { | ||||
|             m_var = m.mk_fresh_const("x", x->get_sort());  | ||||
|         } | ||||
|         expr_ref fml = x->accept(m_var); | ||||
|         if (m.is_true(fml)) { | ||||
|             return l_true; | ||||
|         } | ||||
|         if (m.is_false(fml)) { | ||||
|             return l_false; | ||||
|         } | ||||
|         return m_solver.check_sat(fml); | ||||
|     } | ||||
| 
 | ||||
|     T mk_not(T x) override { | ||||
|         return sym_expr::mk_not(m, x);     | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| re2automaton::re2automaton(ast_manager& m): m(m), u(m), m_ba(nullptr), m_sa(nullptr) {} | ||||
| 
 | ||||
| void re2automaton::set_solver(expr_solver* solver) { | ||||
|     m_solver = solver; | ||||
|     m_ba = alloc(sym_expr_boolean_algebra, m, *solver); | ||||
|     m_sa = alloc(symbolic_automata_t, sm, *m_ba.get()); | ||||
| } | ||||
| 
 | ||||
| eautomaton* re2automaton::mk_product(eautomaton* a1, eautomaton* a2) { | ||||
|     return m_sa->mk_product(*a1, *a2); | ||||
| } | ||||
| 
 | ||||
| eautomaton* re2automaton::operator()(expr* e) {  | ||||
|     eautomaton* r = re2aut(e);  | ||||
|     if (r) {         | ||||
|         r->compress();  | ||||
|         bool_rewriter br(m); | ||||
|         TRACE(seq, display_expr1 disp(m); r->display(tout << mk_pp(e, m) << " -->\n", disp);); | ||||
|     } | ||||
|     return r; | ||||
| }  | ||||
| 
 | ||||
| bool re2automaton::is_unit_char(expr* e, expr_ref& ch) { | ||||
|     zstring s; | ||||
|     expr* c = nullptr; | ||||
|     if (u.str.is_string(e, s) && s.length() == 1) { | ||||
|         ch = u.mk_char(s[0]); | ||||
|         return true; | ||||
|     } | ||||
|     if (u.str.is_unit(e, c)) { | ||||
|         ch = c; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| eautomaton* re2automaton::re2aut(expr* e) { | ||||
|     SASSERT(u.is_re(e)); | ||||
|     expr *e0, *e1, *e2; | ||||
|     scoped_ptr<eautomaton> a, b; | ||||
|     unsigned lo, hi; | ||||
|     zstring s1, s2; | ||||
|     if (u.re.is_to_re(e, e1)) { | ||||
|         return seq2aut(e1); | ||||
|     } | ||||
|     else if (u.re.is_concat(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { | ||||
|         return eautomaton::mk_concat(*a, *b); | ||||
|     } | ||||
|     else if (u.re.is_union(e, e1, e2) && (a = re2aut(e1)) && (b = re2aut(e2))) { | ||||
|         return eautomaton::mk_union(*a, *b); | ||||
|     } | ||||
|     else if (u.re.is_star(e, e1) && (a = re2aut(e1))) { | ||||
|         a->add_final_to_init_moves(); | ||||
|         a->add_init_to_final_states();         | ||||
|         return a.detach();             | ||||
|     } | ||||
|     else if (u.re.is_plus(e, e1) && (a = re2aut(e1))) { | ||||
|         a->add_final_to_init_moves(); | ||||
|         return a.detach();             | ||||
|     } | ||||
|     else if (u.re.is_opt(e, e1) && (a = re2aut(e1))) { | ||||
|         a = eautomaton::mk_opt(*a); | ||||
|         return a.detach();                     | ||||
|     } | ||||
|     else if (u.re.is_range(e, e1, e2)) { | ||||
|         expr_ref _start(m), _stop(m); | ||||
|         if (is_unit_char(e1, _start) && | ||||
|             is_unit_char(e2, _stop)) { | ||||
|             TRACE(seq, tout << "Range: " << _start << " " << _stop << "\n";); | ||||
|             a = alloc(eautomaton, sm, sym_expr::mk_range(_start, _stop)); | ||||
|             return a.detach();             | ||||
|         } | ||||
|         else { | ||||
|             // if e1/e2 are not unit, (re.range e1 e2) is defined to be the empty language
 | ||||
|             return alloc(eautomaton, sm); | ||||
|         } | ||||
|     } | ||||
|     else if (u.re.is_complement(e, e0) && (a = re2aut(e0)) && m_sa) { | ||||
|         return m_sa->mk_complement(*a); | ||||
|     } | ||||
|     else if (u.re.is_loop(e, e1, lo, hi) && (a = re2aut(e1))) { | ||||
|         scoped_ptr<eautomaton> eps = eautomaton::mk_epsilon(sm); | ||||
|         b = eautomaton::mk_epsilon(sm); | ||||
|         while (hi > lo) { | ||||
|             scoped_ptr<eautomaton> c = eautomaton::mk_concat(*a, *b); | ||||
|             b = eautomaton::mk_union(*eps, *c); | ||||
|             --hi; | ||||
|         } | ||||
|         while (lo > 0) { | ||||
|             b = eautomaton::mk_concat(*a, *b); | ||||
|             --lo; | ||||
|         } | ||||
|         return b.detach();         | ||||
|     } | ||||
|     else if (u.re.is_loop(e, e1, lo) && (a = re2aut(e1))) { | ||||
|         b = eautomaton::clone(*a); | ||||
|         b->add_final_to_init_moves(); | ||||
|         b->add_init_to_final_states();         | ||||
|         while (lo > 0) { | ||||
|             b = eautomaton::mk_concat(*a, *b); | ||||
|             --lo; | ||||
|         } | ||||
|         return b.detach();         | ||||
|     } | ||||
|     else if (u.re.is_empty(e)) { | ||||
|         return alloc(eautomaton, sm); | ||||
|     } | ||||
|     else if (u.re.is_full_seq(e)) { | ||||
|         expr_ref tt(m.mk_true(), m); | ||||
|         sort *seq_s = nullptr, *char_s = nullptr; | ||||
|         VERIFY (u.is_re(e->get_sort(), seq_s)); | ||||
|         VERIFY (u.is_seq(seq_s, char_s)); | ||||
|         sym_expr* _true = sym_expr::mk_pred(tt, char_s); | ||||
|         return eautomaton::mk_loop(sm, _true); | ||||
|     } | ||||
|     else if (u.re.is_full_char(e)) { | ||||
|         expr_ref tt(m.mk_true(), m); | ||||
|         sort *seq_s = nullptr, *char_s = nullptr; | ||||
|         VERIFY (u.is_re(e->get_sort(), seq_s)); | ||||
|         VERIFY (u.is_seq(seq_s, char_s)); | ||||
|         sym_expr* _true = sym_expr::mk_pred(tt, char_s); | ||||
|         a = alloc(eautomaton, sm, _true); | ||||
|         return a.detach(); | ||||
|     } | ||||
|     else if (u.re.is_intersection(e, e1, e2) && m_sa && (a = re2aut(e1)) && (b = re2aut(e2))) { | ||||
|         eautomaton* r = m_sa->mk_product(*a, *b); | ||||
|         TRACE(seq, display_expr1 disp(m); a->display(tout << "a:", disp); b->display(tout << "b:", disp); r->display(tout << "intersection:", disp);); | ||||
|         return r; | ||||
|     } | ||||
|     else {         | ||||
|         TRACE(seq, tout << "not handled " << mk_pp(e, m) << "\n";); | ||||
|     } | ||||
|      | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| eautomaton* re2automaton::seq2aut(expr* e) { | ||||
|     SASSERT(u.is_seq(e)); | ||||
|     zstring s; | ||||
|     expr* e1, *e2; | ||||
|     scoped_ptr<eautomaton> a, b; | ||||
|     if (u.str.is_concat(e, e1, e2) && (a = seq2aut(e1)) && (b = seq2aut(e2))) { | ||||
|         return eautomaton::mk_concat(*a, *b); | ||||
|     } | ||||
|     else if (u.str.is_unit(e, e1)) { | ||||
|         return alloc(eautomaton, sm, sym_expr::mk_char(m, e1)); | ||||
|     } | ||||
|     else if (u.str.is_empty(e)) { | ||||
|         return eautomaton::mk_epsilon(sm); | ||||
|     } | ||||
|     else if (u.str.is_string(e, s)) {         | ||||
|         unsigned init = 0; | ||||
|         eautomaton::moves mvs;         | ||||
|         unsigned_vector final; | ||||
|         final.push_back(s.length()); | ||||
|         for (unsigned k = 0; k < s.length(); ++k) { | ||||
|             // reference count?
 | ||||
|             mvs.push_back(eautomaton::move(sm, k, k+1, sym_expr::mk_char(m, u.str.mk_char(s, k)))); | ||||
|         } | ||||
|         return alloc(eautomaton, sm, init, final, mvs); | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| void seq_rewriter::updt_params(params_ref const & p) { | ||||
|     seq_rewriter_params sp(p); | ||||
|  | @ -2721,46 +2405,6 @@ void seq_rewriter::add_next(u_map<expr*>& next, expr_ref_vector& trail, unsigned | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| bool seq_rewriter::is_sequence(eautomaton& aut, expr_ref_vector& seq) { | ||||
|     seq.reset(); | ||||
|     unsigned state = aut.init(); | ||||
|     uint_set visited; | ||||
|     eautomaton::moves mvs; | ||||
|     unsigned_vector states; | ||||
|     aut.get_epsilon_closure(state, states); | ||||
|     bool has_final = false; | ||||
|     for (unsigned i = 0; !has_final && i < states.size(); ++i) { | ||||
|         has_final = aut.is_final_state(states[i]); | ||||
|     } | ||||
|     aut.get_moves_from(state, mvs, true);        | ||||
|     while (!has_final) { | ||||
|         if (mvs.size() != 1) { | ||||
|             return false; | ||||
|         } | ||||
|         if (visited.contains(state)) { | ||||
|             return false; | ||||
|         } | ||||
|         if (aut.is_final_state(mvs[0].src())) { | ||||
|             return false; | ||||
|         } | ||||
|         visited.insert(state); | ||||
|         sym_expr* t = mvs[0].t(); | ||||
|         if (!t || !t->is_char()) { | ||||
|             return false; | ||||
|         } | ||||
|         seq.push_back(str().mk_unit(t->get_char())); | ||||
|         state = mvs[0].dst(); | ||||
|         mvs.reset(); | ||||
|         aut.get_moves_from(state, mvs, true); | ||||
|         states.reset(); | ||||
|         has_final = false; | ||||
|         aut.get_epsilon_closure(state, states); | ||||
|         for (unsigned i = 0; !has_final && i < states.size(); ++i) { | ||||
|             has_final = aut.is_final_state(states[i]); | ||||
|         } | ||||
|     } | ||||
|     return mvs.empty(); | ||||
| } | ||||
| 
 | ||||
| bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { | ||||
|     seq.reset(); | ||||
|  |  | |||
|  | @ -26,8 +26,6 @@ Notes: | |||
| #include "util/params.h" | ||||
| #include "util/lbool.h" | ||||
| #include "util/sign.h" | ||||
| #include "math/automata/automaton.h" | ||||
| #include "math/automata/symbolic_automata.h" | ||||
| 
 | ||||
| 
 | ||||
| inline std::ostream& operator<<(std::ostream& out, expr_ref_pair_vector const& es) { | ||||
|  | @ -81,33 +79,15 @@ public: | |||
|     void dec_ref(sym_expr* s) { if (s) s->dec_ref(); } | ||||
| }; | ||||
| 
 | ||||
| #if 0 | ||||
| 
 | ||||
| class expr_solver { | ||||
| public: | ||||
|     virtual ~expr_solver() = default; | ||||
|     virtual lbool check_sat(expr* e) = 0; | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| typedef automaton<sym_expr, sym_expr_manager> eautomaton; | ||||
| class re2automaton { | ||||
|     typedef boolean_algebra<sym_expr*> boolean_algebra_t; | ||||
|     typedef symbolic_automata<sym_expr, sym_expr_manager> symbolic_automata_t; | ||||
|     ast_manager& m; | ||||
|     sym_expr_manager sm; | ||||
|     seq_util     u;      | ||||
|     scoped_ptr<expr_solver>         m_solver; | ||||
|     scoped_ptr<boolean_algebra_t>   m_ba; | ||||
|     scoped_ptr<symbolic_automata_t> m_sa; | ||||
| 
 | ||||
|     bool is_unit_char(expr* e, expr_ref& ch); | ||||
|     eautomaton* re2aut(expr* e); | ||||
|     eautomaton* seq2aut(expr* e); | ||||
| public: | ||||
|     re2automaton(ast_manager& m); | ||||
|     eautomaton* operator()(expr* e); | ||||
|     void set_solver(expr_solver* solver); | ||||
|     bool has_solver() const { return m_solver; } | ||||
|     eautomaton* mk_product(eautomaton *a1, eautomaton *a2); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|    \brief Cheap rewrite rules for seq constraints | ||||
|  | @ -150,7 +130,7 @@ class seq_rewriter { | |||
|     seq_util       m_util; | ||||
|     arith_util     m_autil; | ||||
|     bool_rewriter  m_br; | ||||
|     re2automaton   m_re2aut; | ||||
|     // re2automaton   m_re2aut;
 | ||||
|     op_cache       m_op_cache; | ||||
|     expr_ref_vector m_es, m_lhs, m_rhs; | ||||
|     bool           m_coalesce_chars;     | ||||
|  | @ -340,7 +320,7 @@ class seq_rewriter { | |||
| 
 | ||||
|     void add_next(u_map<expr*>& next, expr_ref_vector& trail, unsigned idx, expr* cond); | ||||
|     bool is_sequence(expr* e, expr_ref_vector& seq); | ||||
|     bool is_sequence(eautomaton& aut, expr_ref_vector& seq); | ||||
| //    bool is_sequence(eautomaton& aut, expr_ref_vector& seq);
 | ||||
|     bool get_lengths(expr* e, expr_ref_vector& lens, rational& pos); | ||||
|     bool reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); | ||||
|     bool reduce_back(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); | ||||
|  | @ -360,7 +340,8 @@ class seq_rewriter { | |||
| 
 | ||||
| public: | ||||
|     seq_rewriter(ast_manager & m, params_ref const & p = params_ref()): | ||||
|         m_util(m), m_autil(m), m_br(m, p), m_re2aut(m), m_op_cache(m), m_es(m),  | ||||
|         m_util(m), m_autil(m), m_br(m, p), // m_re2aut(m), 
 | ||||
|         m_op_cache(m), m_es(m),  | ||||
|         m_lhs(m), m_rhs(m), m_coalesce_chars(true) { | ||||
|     } | ||||
|     ast_manager & m() const { return m_util.get_manager(); } | ||||
|  | @ -371,8 +352,6 @@ public: | |||
|     void updt_params(params_ref const & p); | ||||
|     static void get_param_descrs(param_descrs & r); | ||||
| 
 | ||||
|     void set_solver(expr_solver* solver) { m_re2aut.set_solver(solver); } | ||||
|     bool has_solver() { return m_re2aut.has_solver(); } | ||||
| 
 | ||||
|     bool coalesce_chars() const { return m_coalesce_chars; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -933,9 +933,6 @@ struct th_rewriter::imp : public rewriter_tpl<th_rewriter_cfg> { | |||
|         return m_cfg.mk_eq(a, b); | ||||
|     } | ||||
| 
 | ||||
|     void set_solver(expr_solver* solver) { | ||||
|         m_cfg.m_seq_rw.set_solver(solver); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| th_rewriter::th_rewriter(ast_manager & m, params_ref const & p): | ||||
|  | @ -1057,10 +1054,6 @@ expr_ref th_rewriter::mk_eq(expr* a, expr* b) { | |||
|     return m_imp->mk_eq(a, b); | ||||
| } | ||||
| 
 | ||||
| void th_rewriter::set_solver(expr_solver* solver) { | ||||
|     m_imp->set_solver(solver); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool th_rewriter::reduce_quantifier(quantifier * old_q,  | ||||
|                                     expr * new_body,  | ||||
|  |  | |||
|  | @ -74,7 +74,6 @@ public: | |||
|     expr_dependency * get_used_dependencies(); | ||||
|     void reset_used_dependencies(); | ||||
| 
 | ||||
|     void set_solver(expr_solver* solver); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -253,10 +253,14 @@ namespace euf { | |||
|         auto n = m_egraph.find(t); | ||||
|         if (!n) | ||||
|             return; | ||||
|         ptr_vector<expr> args; | ||||
|         expr_ref_vector args(m); | ||||
|         expr_mark visited; | ||||
|         for (auto s : enode_class(n)) { | ||||
|             expr_ref r(s->get_expr(), m); | ||||
|             m_rewriter(r); | ||||
|             if (visited.is_marked(r)) | ||||
|                 continue; | ||||
|             visited.mark(r); | ||||
|             args.push_back(r); | ||||
|         } | ||||
|         expr_ref cong(m); | ||||
|  | @ -288,8 +292,10 @@ namespace euf { | |||
|             propagate_rules(); | ||||
|             propagate_closures(); | ||||
|             IF_VERBOSE(11, verbose_stream() << "propagate " << m_stats.m_num_instances << "\n"); | ||||
|             if (!should_stop()) | ||||
|                 propagate_arithmetic(); | ||||
|             if (!m_should_propagate && !should_stop()) | ||||
|                 propagate_all_rules(); | ||||
|                 propagate_all_rules();             | ||||
|         } | ||||
|         TRACE(euf, m_egraph.display(tout)); | ||||
|     } | ||||
|  | @ -310,16 +316,14 @@ namespace euf { | |||
|             for (auto* ch : enode_args(n)) | ||||
|                 m_nodes_to_canonize.push_back(ch); | ||||
|             }; | ||||
|         expr* x = nullptr, * y = nullptr; | ||||
|         expr* x = nullptr, * y = nullptr, * nf = nullptr; | ||||
|         if (m.is_eq(f, x, y)) { | ||||
|             expr_ref x1(x, m); | ||||
|             expr_ref y1(y, m); | ||||
|             m_rewriter(x1); | ||||
|             m_rewriter(y1); | ||||
| 
 | ||||
|             add_quantifiers(x1); | ||||
|             add_quantifiers(x); | ||||
|             add_quantifiers(y1); | ||||
|             enode* a = mk_enode(x1); | ||||
|             enode* a = mk_enode(x); | ||||
|             enode* b = mk_enode(y1); | ||||
| 
 | ||||
|             if (a->get_root() == b->get_root()) | ||||
|  | @ -331,42 +335,28 @@ namespace euf { | |||
|             m_egraph.merge(a, b, to_ptr(push_pr_dep(pr, d))); | ||||
|             m_egraph.propagate(); | ||||
|             m_should_propagate = true;  | ||||
| 
 | ||||
| #if 0 | ||||
|             auto a1 = mk_enode(x); | ||||
|             auto b1 = mk_enode(y); | ||||
| 
 | ||||
|             if (a->get_root() != a1->get_root()) { | ||||
|                 add_children(a1);; | ||||
|                 m_egraph.merge(a, a1, nullptr); | ||||
|                 m_egraph.propagate(); | ||||
|             } | ||||
| 
 | ||||
|             if (b->get_root() != b1->get_root()) { | ||||
|                 add_children(b1); | ||||
|                 m_egraph.merge(b, b1, nullptr); | ||||
|                 m_egraph.propagate(); | ||||
|             } | ||||
| #endif | ||||
|              | ||||
|             if (m_side_condition_solver && a->get_root() != b->get_root()) | ||||
|                 m_side_condition_solver->add_constraint(f, pr, d); | ||||
|             IF_VERBOSE(1, verbose_stream() << "eq: " << a->get_root_id() << " " << b->get_root_id() << " "  | ||||
|                  << x1 << " == " << y1 << "\n"); | ||||
|                  << mk_pp(x, m) << " == " << y1 << "\n"); | ||||
|         } | ||||
|         else if (m.is_not(f, f)) { | ||||
|             enode* n = mk_enode(f); | ||||
|         else if (m.is_not(f, nf)) { | ||||
|             expr_ref f1(nf, m); | ||||
|             m_rewriter(f1); | ||||
|             enode* n = mk_enode(f1); | ||||
|             if (m.is_false(n->get_root()->get_expr())) | ||||
|                 return; | ||||
|             add_quantifiers(f); | ||||
|             add_quantifiers(f1); | ||||
|             auto n_false = mk_enode(m.mk_false()); | ||||
|             auto j = to_ptr(push_pr_dep(pr, d)); | ||||
|             m_egraph.new_diseq(n, j); | ||||
|             m_egraph.merge(n, n_false, j); | ||||
|             m_egraph.propagate(); | ||||
|             add_children(n); | ||||
|             m_should_propagate = true; | ||||
|             if (m_side_condition_solver) | ||||
|                 m_side_condition_solver->add_constraint(f, pr, d); | ||||
|             IF_VERBOSE(1, verbose_stream() << "not: " << mk_pp(f, m) << "\n"); | ||||
|             IF_VERBOSE(1, verbose_stream() << "not: " << nf << "\n"); | ||||
|         } | ||||
|         else { | ||||
|             enode* n = mk_enode(f); | ||||
|  | @ -631,6 +621,88 @@ namespace euf { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     //
 | ||||
|     // extract shared arithmetic terms T
 | ||||
|     // extract shared variables V
 | ||||
|     // add t = rewriter(t) to E-graph
 | ||||
|     // solve for V by solver producing theta
 | ||||
|     // add theta to E-graph
 | ||||
|     // add theta to canonize (?)
 | ||||
|     //
 | ||||
|     void completion::propagate_arithmetic() { | ||||
|         ptr_vector<expr> shared_terms, shared_vars; | ||||
|         expr_mark visited; | ||||
|         arith_util a(m); | ||||
|         bool merged = false; | ||||
|         for (auto n : m_egraph.nodes()) { | ||||
|             expr* e = n->get_expr(); | ||||
|             if (!is_app(e)) | ||||
|                 continue; | ||||
|             app* t = to_app(e); | ||||
|             bool is_arith = a.is_arith_expr(t); | ||||
|             for (auto arg : *t) { | ||||
|                 bool is_arith_arg = a.is_arith_expr(arg); | ||||
|                 if (is_arith_arg == is_arith) | ||||
|                     continue; | ||||
|                 if (visited.is_marked(arg)) | ||||
|                     continue; | ||||
|                 visited.mark(arg); | ||||
|                 if (is_arith_arg) | ||||
|                     shared_terms.push_back(arg); | ||||
|                 else | ||||
|                     shared_vars.push_back(arg); | ||||
|             }            | ||||
|         } | ||||
|         for (auto t : shared_terms) { | ||||
|             auto tn = m_egraph.find(t); | ||||
|              | ||||
|             if (!tn) | ||||
|                 continue; | ||||
|             expr_ref r(t, m); | ||||
|             m_rewriter(r); | ||||
|             if (r == t) | ||||
|                 continue; | ||||
|             auto n = m_egraph.find(t); | ||||
|             auto t_root = tn->get_root(); | ||||
|             if (n && n->get_root() == t_root)  | ||||
|                 continue; | ||||
|              | ||||
|             if (!n) | ||||
|                 n = mk_enode(r); | ||||
|             TRACE(euf_completion, tout << "propagate-arith: " << mk_pp(t, m) << " -> " << r << "\n"); | ||||
|              | ||||
|             m_egraph.merge(tn, n, nullptr); | ||||
|             merged = true; | ||||
|         } | ||||
|         visited.reset(); | ||||
|         for (auto v : shared_vars) { | ||||
|             if (visited.is_marked(v)) | ||||
|                 continue; | ||||
|             visited.mark(v); | ||||
|             vector<side_condition_solver::solution> sol; | ||||
|             expr_ref term(m), guard(m); | ||||
|             sol.push_back({ v, term, guard }); | ||||
|             m_side_condition_solver->solve_for(sol); | ||||
|             for (auto [v, t, g] : sol) { | ||||
|                 if (!t) | ||||
|                     continue; | ||||
|                 visited.mark(v); | ||||
|                 auto a = mk_enode(v); | ||||
|                 auto b = mk_enode(t); | ||||
|                 if (a->get_root() == b->get_root()) | ||||
|                     continue; | ||||
|                 TRACE(euf_completion, tout << "propagate-arith: " << m_egraph.bpp(a) << " -> " << m_egraph.bpp(b) << "\n"); | ||||
|                 IF_VERBOSE(1, verbose_stream() << "propagate-arith: " << m_egraph.bpp(a) << " -> " << m_egraph.bpp(b) << "\n"); | ||||
|                 m_egraph.merge(a, b, nullptr); // TODO guard justifies reason.
 | ||||
|                 merged = true; | ||||
|             } | ||||
|         } | ||||
|         if (merged) { | ||||
|             m_egraph.propagate(); | ||||
|             m_should_propagate = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void completion::propagate_closures() { | ||||
|         for (auto [q, clos] : m_closures) { | ||||
|             expr* body = clos.second; | ||||
|  |  | |||
|  | @ -187,6 +187,7 @@ namespace euf { | |||
|         expr_ref get_canonical(quantifier* q, proof_ref& pr, expr_dependency_ref& d); | ||||
|         obj_map<quantifier, std::pair<ptr_vector<expr>, expr*>> m_closures; | ||||
| 
 | ||||
|         void propagate_arithmetic(); | ||||
|         expr_dependency* explain_eq(enode* a, enode* b); | ||||
|         proof_ref prove_eq(enode* a, enode* b); | ||||
|         proof_ref prove_conflict(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue