mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	First pass at free variable elimination
This commit is contained in:
		
							parent
							
								
									4cdd3bf77d
								
							
						
					
					
						commit
						29695391de
					
				
					 5 changed files with 144 additions and 21 deletions
				
			
		|  | @ -62,11 +62,13 @@ namespace polysat { | ||||||
|     class conflict_resolver { |     class conflict_resolver { | ||||||
|         inf_saturate m_saturate; |         inf_saturate m_saturate; | ||||||
|         ex_polynomial_superposition m_poly_sup; |         ex_polynomial_superposition m_poly_sup; | ||||||
|  |         free_variable_elimination m_free_variable_elimination; | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|         conflict_resolver(solver& s) |         conflict_resolver(solver& s) | ||||||
|             : m_saturate(s) |             : m_saturate(s) | ||||||
|             , m_poly_sup(s) |             , m_poly_sup(s) | ||||||
|  |             , m_free_variable_elimination(s) | ||||||
|         {} |         {} | ||||||
| 
 | 
 | ||||||
|         bool try_resolve_value(pvar v, conflict& core) { |         bool try_resolve_value(pvar v, conflict& core) { | ||||||
|  | @ -76,6 +78,11 @@ namespace polysat { | ||||||
|                 return true; |                 return true; | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // Analyse current conflict core to extract additional lemmas
 | ||||||
|  |         void find_extra_lemmas(conflict& core) { | ||||||
|  |             m_free_variable_elimination.find_lemma(core); | ||||||
|  |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     class header_with_var : public displayable { |     class header_with_var : public displayable { | ||||||
|  | @ -524,6 +531,10 @@ namespace polysat { | ||||||
|     clause_ref conflict::build_lemma() { |     clause_ref conflict::build_lemma() { | ||||||
|         LOG_H3("Build lemma from core"); |         LOG_H3("Build lemma from core"); | ||||||
|         LOG("core: " << *this); |         LOG("core: " << *this); | ||||||
|  | 
 | ||||||
|  |         // TODO: do this after each step?
 | ||||||
|  |         m_resolver->find_extra_lemmas(*this); | ||||||
|  | 
 | ||||||
|         clause_builder lemma(s); |         clause_builder lemma(s); | ||||||
| 
 | 
 | ||||||
| #if 0 | #if 0 | ||||||
|  |  | ||||||
|  | @ -258,6 +258,7 @@ namespace polysat { | ||||||
| 
 | 
 | ||||||
|         void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(s, us, dep, is_positive()); } |         void add_to_univariate_solver(solver& s, univariate_solver& us, unsigned dep) const { get()->add_to_univariate_solver(s, us, dep, is_positive()); } | ||||||
| 
 | 
 | ||||||
|  |         unsigned_vector const& vars() const { return m_constraint->vars(); } | ||||||
|         unsigned var(unsigned idx) const { return m_constraint->var(idx); } |         unsigned var(unsigned idx) const { return m_constraint->var(idx); } | ||||||
|         bool contains_var(pvar v) const { return m_constraint->contains_var(v); } |         bool contains_var(pvar v) const { return m_constraint->contains_var(v); } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -85,6 +85,7 @@ namespace polysat { | ||||||
|         friend class assignment_pp; |         friend class assignment_pp; | ||||||
|         friend class assignments_pp; |         friend class assignments_pp; | ||||||
|         friend class ex_polynomial_superposition; |         friend class ex_polynomial_superposition; | ||||||
|  |         friend class free_variable_elimination; | ||||||
|         friend class inf_saturate; |         friend class inf_saturate; | ||||||
|         friend class constraint_manager; |         friend class constraint_manager; | ||||||
|         friend class scoped_solverv; |         friend class scoped_solverv; | ||||||
|  |  | ||||||
|  | @ -8,26 +8,134 @@ Module Name: | ||||||
| Author: | Author: | ||||||
| 
 | 
 | ||||||
|     Nikolaj Bjorner (nbjorner) 2021-03-19 |     Nikolaj Bjorner (nbjorner) 2021-03-19 | ||||||
|     Jakob Rath 2021-04-6 |     Jakob Rath 2021-04-06 | ||||||
| 
 | 
 | ||||||
| --*/ | --*/ | ||||||
| #include "math/polysat/variable_elimination.h" | #include "math/polysat/variable_elimination.h" | ||||||
|  | #include "math/polysat/conflict.h" | ||||||
|  | #include "math/polysat/clause_builder.h" | ||||||
| #include "math/polysat/solver.h" | #include "math/polysat/solver.h" | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| 
 | 
 | ||||||
| namespace polysat { | namespace polysat { | ||||||
| 
 | 
 | ||||||
|     bool ve_reduction::perform(solver& s, pvar v, conflict& core) { |     void free_variable_elimination::find_lemma(conflict& core) { | ||||||
|         // without any further hints, we just do core reduction with the stronger premise "C contains a c' that evaluates to false",
 |         LOG_H1("Free Variable Elimination"); | ||||||
|         // and kick out all other constraints.
 |         LOG("core: " << core); | ||||||
|  |         LOG("Free variables: " << s.m_free_pvars); | ||||||
|  |         for (pvar v : core.vars_occurring_in_constraints()) | ||||||
|  |             if (!s.is_assigned(v))  // TODO: too restrictive. should also consider variables that will be unassigned only after backjumping (can update this after assignment handling in search state is refactored.)
 | ||||||
|  |                 find_lemma(v, core); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void free_variable_elimination::find_lemma(pvar v, conflict& core) { | ||||||
|  |         LOG_H2("Free Variable Elimination for v" << v); | ||||||
|  |         // find constraint that allows computing v from other variables
 | ||||||
|  |         // (currently, consider only equations that contain v with degree 1)
 | ||||||
|         for (signed_constraint c : core) { |         for (signed_constraint c : core) { | ||||||
|             if (!c->contains_var(v) && c.is_currently_false(s)) { |             if (!c.is_eq()) | ||||||
|                 core.set(c); |                 continue; | ||||||
|                 core.logger().log("ve_reduction"); |             if (c.eq().degree(v) != 1) | ||||||
|                 return true; |                 continue; | ||||||
|             }                 |             find_lemma(v, c, core); | ||||||
|         } |         } | ||||||
|         return false; |     } | ||||||
|  | 
 | ||||||
|  |     void free_variable_elimination::find_lemma(pvar v, signed_constraint c, conflict& core) { | ||||||
|  |         LOG_H3("Free Variable Elimination for v" << v << " using equation " << c); | ||||||
|  |         pdd const& p = c.eq(); | ||||||
|  |         SASSERT_EQ(p.degree(v), 1); | ||||||
|  |         auto& m = p.manager(); | ||||||
|  |         pdd lc = m.zero(); | ||||||
|  |         pdd rest = m.zero(); | ||||||
|  |         p.factor(v, 1, lc, rest); | ||||||
|  |         if (rest.is_val()) | ||||||
|  |             return; | ||||||
|  |         // lc * v + rest == p == 0
 | ||||||
|  |         // v == -1 * rest * lc^-1
 | ||||||
|  | 
 | ||||||
|  |         SASSERT(!lc.free_vars().contains(v)); | ||||||
|  |         SASSERT(!rest.free_vars().contains(v)); | ||||||
|  | 
 | ||||||
|  |         LOG("lc: " << lc); | ||||||
|  |         LOG("rest: " << rest); | ||||||
|  | 
 | ||||||
|  |         assignment_t a; | ||||||
|  |         pdd const lcs = eval(lc, core, a); | ||||||
|  |         LOG("lcs: " << lcs); | ||||||
|  |         pdd lci = m.zero(); | ||||||
|  |         if (!inv(lcs, lci)) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         pdd const rs = s.subst(a, rest); | ||||||
|  |         pdd const vs = -rs * lci;  // this is the polynomial that computes v
 | ||||||
|  |         LOG("vs: " << vs); | ||||||
|  |         SASSERT(!vs.free_vars().contains(v)); | ||||||
|  | 
 | ||||||
|  |         // Find another constraint where we want to substitute v
 | ||||||
|  |         for (signed_constraint c_target : core) { | ||||||
|  |             if (c == c_target) | ||||||
|  |                 continue; | ||||||
|  |             if (c_target.vars().size() <= 1) | ||||||
|  |                 continue; | ||||||
|  |             if (!c_target.contains_var(v)) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             // TODO: helper method constraint::subst(pvar v, pdd const& p)
 | ||||||
|  |             //       (or rather, add it on constraint_manager since we need to allocate/dedup the new constraint)
 | ||||||
|  |             //  For now, just restrict to ule_constraint.
 | ||||||
|  |             if (!c_target->is_ule()) | ||||||
|  |                 continue; | ||||||
|  |             // TODO: maybe apply assignment a here as well
 | ||||||
|  |             pdd const lhs = c_target->to_ule().lhs().subst_pdd(v, vs); | ||||||
|  |             pdd const rhs = c_target->to_ule().rhs().subst_pdd(v, vs); | ||||||
|  |             signed_constraint c_new = s.ule(lhs, rhs); | ||||||
|  |             if (c_target.is_negative()) | ||||||
|  |                 c_new.negate(); | ||||||
|  |             LOG("c_target: " << lit_pp(s, c_target)); | ||||||
|  |             LOG("c_new:    " << lit_pp(s, c_new)); | ||||||
|  | 
 | ||||||
|  |             clause_builder cb(s); | ||||||
|  |             for (auto [w, wv] : a) | ||||||
|  |                 cb.push(~s.eq(s.var(w), wv)); | ||||||
|  |             cb.push(~c); | ||||||
|  |             cb.push(~c_target); | ||||||
|  |             cb.push(c_new); | ||||||
|  |             core.add_lemma(cb.build()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Evaluate p under assignments in the core.
 | ||||||
|  |     pdd free_variable_elimination::eval(pdd const& p, conflict& core, assignment_t& out_assignment) { | ||||||
|  |         // TODO: this should probably be a helper method on conflict.
 | ||||||
|  |         // TODO: recognize constraints of the form "v1 == 27" to be used in the assignment?
 | ||||||
|  |         //       (but maybe useful evaluations are always part of core.vars() anyway?)
 | ||||||
|  | 
 | ||||||
|  |         assignment_t& a = out_assignment; | ||||||
|  |         SASSERT(a.empty()); | ||||||
|  | 
 | ||||||
|  |         for (auto v : p.free_vars()) | ||||||
|  |             if (core.contains_pvar(v)) | ||||||
|  |                 a.push_back({v, s.get_value(v)}); | ||||||
|  | 
 | ||||||
|  |         pdd q = s.subst(a, p); | ||||||
|  | 
 | ||||||
|  |         // TODO: like in the old conflict::minimize_vars, we can now try to remove unnecessary variables from a.
 | ||||||
|  | 
 | ||||||
|  |         return q; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Compute the multiplicative inverse of p.
 | ||||||
|  |     bool free_variable_elimination::inv(pdd const& p, pdd& out_p_inv) { | ||||||
|  |         // TODO: in the non-val case, we could introduce an additional variable to represent the inverse
 | ||||||
|  |         //       (and a constraint p * p_inv == 1)
 | ||||||
|  |         if (!p.is_val()) | ||||||
|  |             return false; | ||||||
|  |         rational iv; | ||||||
|  |         if (!p.val().mult_inverse(p.power_of_2(), iv)) | ||||||
|  |             return false; | ||||||
|  |         out_p_inv = p.manager().mk_val(iv); | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,25 +8,27 @@ Module Name: | ||||||
| Author: | Author: | ||||||
| 
 | 
 | ||||||
|     Nikolaj Bjorner (nbjorner) 2021-03-19 |     Nikolaj Bjorner (nbjorner) 2021-03-19 | ||||||
|     Jakob Rath 2021-04-6 |     Jakob Rath 2021-04-06 | ||||||
| 
 | 
 | ||||||
| --*/ | --*/ | ||||||
| #pragma once | #pragma once | ||||||
| #include "math/polysat/conflict.h" | 
 | ||||||
|  | #include "math/polysat/types.h" | ||||||
|  | #include "math/polysat/constraint.h" | ||||||
| 
 | 
 | ||||||
| namespace polysat { | namespace polysat { | ||||||
| 
 | 
 | ||||||
|     class solver; |     class conflict; | ||||||
| 
 | 
 | ||||||
|     class variable_elimination_engine { |     class free_variable_elimination { | ||||||
|  |         solver& s; | ||||||
|  |         void find_lemma(pvar v, conflict& core); | ||||||
|  |         void find_lemma(pvar v, signed_constraint c, conflict& core); | ||||||
|  |         pdd eval(pdd const& p, conflict& core, assignment_t& out_assignment); | ||||||
|  |         bool inv(pdd const& p, pdd& out_p_inv); | ||||||
|     public: |     public: | ||||||
|         virtual ~variable_elimination_engine() {} |         free_variable_elimination(solver& s): s(s) {} | ||||||
|         virtual bool perform(solver& s, pvar v, conflict& core) = 0; |         void find_lemma(conflict& core); | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     class ve_reduction : public variable_elimination_engine { |  | ||||||
|     public: |  | ||||||
|         bool perform(solver& s, pvar v, conflict& core) override; |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue