mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32: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"), |                           ('coalesce_rules', BOOL, False, "BMC: coalesce rules"), | ||||||
|                           ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), |                           ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), | ||||||
|                           ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"), |                           ('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"), |                           ('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 " |                           ('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when " | ||||||
|                                                                         "checking for reachability (not only during cube weakening)"), |                                                                         "checking for reachability (not only during cube weakening)"), | ||||||
|  |  | ||||||
|  | @ -1477,6 +1477,10 @@ namespace pdr { | ||||||
|         if (m_params.inductive_reachability_check()) { |         if (m_params.inductive_reachability_check()) { | ||||||
|             m_core_generalizers.push_back(alloc(core_induction_generalizer, *this)); |             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 { |     void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info>& rs) const { | ||||||
|  |  | ||||||
|  | @ -28,7 +28,9 @@ Revision History: | ||||||
| namespace pdr { | namespace pdr { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     //
 |     // ------------------------
 | ||||||
|  |     // core_bool_inductive_generalizer
 | ||||||
|  | 
 | ||||||
|     // main propositional induction generalizer.
 |     // main propositional induction generalizer.
 | ||||||
|     // drop literals one by one from the core and check if the core is still inductive.
 |     // 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:
 |     // for each disjunct of core:
 | ||||||
|     //     weaken predecessor.
 |     //     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); |         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 { |     class core_farkas_generalizer : public core_generalizer { | ||||||
|         farkas_learner m_farkas_learner; |         farkas_learner m_farkas_learner; | ||||||
|     public: |     public: | ||||||
|  |  | ||||||
|  | @ -88,7 +88,6 @@ namespace pdr { | ||||||
|                   for (unsigned i = 0; i < assumptions.size(); ++i) { |                   for (unsigned i = 0; i < assumptions.size(); ++i) { | ||||||
|                       pp.add_assumption(assumptions[i].get()); |                       pp.add_assumption(assumptions[i].get()); | ||||||
|                   } |                   } | ||||||
|                   pp.display_smt2(tout, m.mk_true()); |  | ||||||
| 
 | 
 | ||||||
|                   static unsigned lemma_id = 0; |                   static unsigned lemma_id = 0; | ||||||
|                   std::ostringstream strm; |                   std::ostringstream strm; | ||||||
|  | @ -97,6 +96,7 @@ namespace pdr { | ||||||
|                   pp.display_smt2(out, m.mk_true()); |                   pp.display_smt2(out, m.mk_true()); | ||||||
|                   out.close(); |                   out.close(); | ||||||
|                   lemma_id++; |                   lemma_id++; | ||||||
|  |                   tout << "pdr_check: " << strm.str() << "\n"; | ||||||
|               }); |               }); | ||||||
|         lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); |         lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); | ||||||
|         if (!m.is_true(m_pred)) { |         if (!m.is_true(m_pred)) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue