mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-30 19:22:28 +00:00 
			
		
		
		
	experiment with arithmetic core generalizers
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									23c5c94311
								
							
						
					
					
						commit
						2e2fa84d40
					
				
					 5 changed files with 195 additions and 2 deletions
				
			
		|  | @ -44,6 +44,7 @@ def_module_params('fixedpoint', | |||
|                           ('coalesce_rules', BOOL, False, "BMC: coalesce rules"), | ||||
|                           ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), | ||||
|                           ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"), | ||||
|                           ('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"), | ||||
|                           ('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"), | ||||
|                           ('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when " | ||||
|                                                                         "checking for reachability (not only during cube weakening)"), | ||||
|  |  | |||
|  | @ -1477,6 +1477,10 @@ namespace pdr { | |||
|         if (m_params.inductive_reachability_check()) { | ||||
|             m_core_generalizers.push_back(alloc(core_induction_generalizer, *this)); | ||||
|         } | ||||
|         if (m_params.use_arith_inductive_generalizer()) { | ||||
|             m_core_generalizers.push_back(alloc(core_arith_inductive_generalizer, *this)); | ||||
|         } | ||||
|          | ||||
|     } | ||||
| 
 | ||||
|     void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info>& rs) const { | ||||
|  |  | |||
|  | @ -28,7 +28,9 @@ Revision History: | |||
| namespace pdr { | ||||
| 
 | ||||
| 
 | ||||
|     //
 | ||||
|     // ------------------------
 | ||||
|     // core_bool_inductive_generalizer
 | ||||
| 
 | ||||
|     // main propositional induction generalizer.
 | ||||
|     // drop literals one by one from the core and check if the core is still inductive.
 | ||||
|     //    
 | ||||
|  | @ -97,6 +99,9 @@ namespace pdr { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // ------------------------
 | ||||
|     // core_farkas_generalizer 
 | ||||
| 
 | ||||
|     // 
 | ||||
|     // for each disjunct of core:
 | ||||
|     //     weaken predecessor.
 | ||||
|  | @ -142,6 +147,158 @@ namespace pdr { | |||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // -----------------------------
 | ||||
|     // core_arith_inductive_generalizer 
 | ||||
| 
 | ||||
|     core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx): | ||||
|         core_generalizer(ctx),  | ||||
|         m(ctx.get_manager()), | ||||
|         a(m), | ||||
|         m_refs(m) {} | ||||
| 
 | ||||
|     void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { | ||||
|         if (core.size() <= 1) { | ||||
|             return; | ||||
|         } | ||||
|         reset(); | ||||
|         expr_ref e(m), t1(m), t2(m), t3(m); | ||||
|         rational r; | ||||
|          | ||||
|         TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); | ||||
| 
 | ||||
|         svector<eq> eqs; | ||||
|         get_eqs(core, eqs); | ||||
|          | ||||
|         for (unsigned eq = 0; eq < eqs.size(); ++eq) { | ||||
|             rational r = eqs[eq].m_value; | ||||
|             expr* x = eqs[eq].m_term; | ||||
|             unsigned k = eqs[eq].m_i; | ||||
|             unsigned l = eqs[eq].m_j; | ||||
| 
 | ||||
|             expr_ref_vector new_core(m); | ||||
|             for (unsigned i = 0; i < core.size(); ++i) { | ||||
|                 if (i == k || k == l) { | ||||
|                     new_core.push_back(m.mk_true()); | ||||
|                 } | ||||
|                 else { | ||||
|                     if (!substitute_alias(r, x, core[i].get(), e)) { | ||||
|                         e = core[i].get(); | ||||
|                     } | ||||
|                     new_core.push_back(e); | ||||
|                 } | ||||
|             } | ||||
|             if (abs(r) >= rational(2) && a.is_int(x)) { | ||||
|                 new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(abs(r), true)), a.mk_numeral(rational(0), true)); | ||||
|                 new_core[l] = a.mk_ge(x, a.mk_numeral(r, true)); | ||||
|             } | ||||
| 
 | ||||
|             bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level); | ||||
| 
 | ||||
|             IF_VERBOSE(1,  | ||||
|                        verbose_stream() << (inductive?"":"non") << "inductive\n"; | ||||
|                        for (unsigned j = 0; j < new_core.size(); ++j) {  | ||||
|                            verbose_stream() << mk_pp(new_core[j].get(), m) << "\n";  | ||||
|                        }); | ||||
| 
 | ||||
|             if (inductive) { | ||||
|                 core.reset(); | ||||
|                 core.append(new_core); | ||||
|             } | ||||
|         } | ||||
|     }     | ||||
| 
 | ||||
|     void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) { | ||||
|         if (r.is_neg()) { | ||||
|             expr_ref e(m); | ||||
|             e = a.mk_uminus(x); | ||||
|             m_refs.push_back(e); | ||||
|             x = e; | ||||
|             is_lower = !is_lower; | ||||
|         } | ||||
| 
 | ||||
|         if (is_lower) { | ||||
|             m_lb.insert(abs(r), std::make_pair(x, i)); | ||||
|         } | ||||
|         else { | ||||
|             m_ub.insert(abs(r), std::make_pair(x, i)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void core_arith_inductive_generalizer::reset() { | ||||
|         m_refs.reset(); | ||||
|         m_lb.reset(); | ||||
|         m_ub.reset(); | ||||
|     } | ||||
| 
 | ||||
|     void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector<eq>& eqs) { | ||||
|         expr* e1, *x, *y; | ||||
|         expr_ref e(m); | ||||
|         rational r; | ||||
| 
 | ||||
|         for (unsigned i = 0; i < core.size(); ++i) { | ||||
|             e = core[i]; | ||||
|             if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { | ||||
|                 // not (<= x r) <=> x >= r + 1
 | ||||
|                 insert_bound(true, x, r + rational(1), i); | ||||
|             } | ||||
|             else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { | ||||
|                 // not (>= x r) <=> x <= r - 1
 | ||||
|                 insert_bound(false, x, r - rational(1), i); | ||||
|             } | ||||
|             else if (a.is_le(e, x, y) && a.is_numeral(y, r)) { | ||||
|                 insert_bound(false, x, r, i); | ||||
|             } | ||||
|             else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) { | ||||
|                 insert_bound(true, x, r, i); | ||||
|             } | ||||
|         } | ||||
|         bounds_t::iterator it = m_lb.begin(), end = m_lb.end(); | ||||
|         for (; it != end; ++it) { | ||||
|             rational r = it->m_key; | ||||
|             vector<term_loc_t> & terms1 = it->m_value; | ||||
|             vector<term_loc_t> terms2; | ||||
|             if (r >= rational(2) && m_ub.find(r, terms2)) { | ||||
|                 bool done = false; | ||||
|                 for (unsigned i = 0; !done && i < terms1.size(); ++i) { | ||||
|                     for (unsigned j = 0; !done && j < terms2.size(); ++j) { | ||||
|                         expr* t1 = terms1[i].first; | ||||
|                         expr* t2 = terms2[j].first; | ||||
|                         if (t1 == t2) { | ||||
|                             eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); | ||||
|                             done = true; | ||||
|                         } | ||||
|                         else { | ||||
|                             e = m.mk_eq(t1, t2); | ||||
|                             th_rewriter rw(m); | ||||
|                             rw(e); | ||||
|                             if (m.is_true(e)) { | ||||
|                                 eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); | ||||
|                                 done = true;                                 | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) { | ||||
|         rational r2; | ||||
|         expr* y, *z, *e1; | ||||
|         if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) { | ||||
|             result = m.mk_not(result); | ||||
|             return true; | ||||
|         } | ||||
|         if (a.is_le(e, y, z) && a.is_numeral(z, r2) && r == r2) { | ||||
|             result = a.mk_le(y, x); | ||||
|             return true; | ||||
|         } | ||||
|         if (a.is_ge(e, y, z) && a.is_numeral(z, r2) && r == r2) { | ||||
|             result = a.mk_ge(y, x); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // 
 | ||||
|  |  | |||
|  | @ -33,6 +33,37 @@ namespace pdr { | |||
|         virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); | ||||
|     }; | ||||
| 
 | ||||
|     template <typename T> | ||||
|     class r_map : public map<rational, T, rational::hash_proc, rational::eq_proc> { | ||||
|     }; | ||||
| 
 | ||||
|     class core_arith_inductive_generalizer : public core_generalizer { | ||||
|         typedef std::pair<expr*, unsigned> term_loc_t; | ||||
|         typedef r_map<vector<term_loc_t> > bounds_t; | ||||
| 
 | ||||
|         ast_manager&    m; | ||||
|         arith_util      a; | ||||
|         expr_ref_vector m_refs; | ||||
|         bounds_t        m_lb; | ||||
|         bounds_t        m_ub; | ||||
| 
 | ||||
|         struct eq { | ||||
|             expr*    m_term; | ||||
|             rational m_value; | ||||
|             unsigned m_i; | ||||
|             unsigned m_j; | ||||
|             eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {} | ||||
|         }; | ||||
|         void reset(); | ||||
|         void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i); | ||||
|         void get_eqs(expr_ref_vector const& core, svector<eq>& eqs); | ||||
|         bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result); | ||||
|     public: | ||||
|         core_arith_inductive_generalizer(context& ctx); | ||||
|         virtual ~core_arith_inductive_generalizer() {} | ||||
|         virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); | ||||
|     }; | ||||
| 
 | ||||
|     class core_farkas_generalizer : public core_generalizer { | ||||
|         farkas_learner m_farkas_learner; | ||||
|     public: | ||||
|  |  | |||
|  | @ -88,7 +88,6 @@ namespace pdr { | |||
|                   for (unsigned i = 0; i < assumptions.size(); ++i) { | ||||
|                       pp.add_assumption(assumptions[i].get()); | ||||
|                   } | ||||
|                   pp.display_smt2(tout, m.mk_true()); | ||||
| 
 | ||||
|                   static unsigned lemma_id = 0; | ||||
|                   std::ostringstream strm; | ||||
|  | @ -97,6 +96,7 @@ namespace pdr { | |||
|                   pp.display_smt2(out, m.mk_true()); | ||||
|                   out.close(); | ||||
|                   lemma_id++; | ||||
|                   tout << "pdr_check: " << strm.str() << "\n"; | ||||
|               }); | ||||
|         lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); | ||||
|         if (!m.is_true(m_pred)) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue