mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	add assume-eqs and extensionality
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									981c7d27ea
								
							
						
					
					
						commit
						df62e5e9e6
					
				
					 6 changed files with 98 additions and 22 deletions
				
			
		|  | @ -70,6 +70,8 @@ void finite_set_decl_plugin::init() { | |||
|     m_sigs[OP_FINITE_SET_MAP]        = alloc(polymorphism::psig, m, "set.map",        2, 2, arrABsetA, setB); | ||||
|     m_sigs[OP_FINITE_SET_SELECT]     = alloc(polymorphism::psig, m, "set.select",     1, 2, arrABoolsetA, setA); | ||||
|     m_sigs[OP_FINITE_SET_RANGE]      = alloc(polymorphism::psig, m, "set.range",      0, 2, intintT, setInt); | ||||
|     m_sigs[OP_FINITE_SET_DIFF]       = alloc(polymorphism::psig, m, "set.diff", 1, 2, setAsetA, A); | ||||
| //    m_sigs[OP_FINITE_SET_MAP_INVERSE] = alloc(polymorphism::psig, m, "set.map_inverse", 2, 3, arrABsetBsetA, A);
 | ||||
| } | ||||
| 
 | ||||
| sort * finite_set_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { | ||||
|  | @ -152,6 +154,7 @@ func_decl * finite_set_decl_plugin::mk_func_decl(decl_kind k, unsigned num_param | |||
|     case OP_FINITE_SET_MAP: | ||||
|     case OP_FINITE_SET_SELECT: | ||||
|     case OP_FINITE_SET_RANGE: | ||||
|     case OP_FINITE_SET_DIFF: | ||||
|         return mk_finite_set_op(k, arity, domain, range); | ||||
|     default: | ||||
|         return nullptr; | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ Operators: | |||
|     set.map : (S -> T) (FiniteSet S) -> (FiniteSet T) | ||||
|     set.select : (S -> Bool) (FiniteSet S) -> (FiniteSet S) | ||||
|     set.range : Int Int -> (FiniteSet Int) | ||||
|     set.diff : (FiniteSet S) (FiniteSet S) -> S | ||||
|     | ||||
| --*/ | ||||
| #pragma once | ||||
|  | @ -46,6 +47,8 @@ enum finite_set_op_kind { | |||
|     OP_FINITE_SET_MAP, | ||||
|     OP_FINITE_SET_SELECT, | ||||
|     OP_FINITE_SET_RANGE, | ||||
|     OP_FINITE_SET_DIFF, | ||||
|     OP_FINITE_SET_MAP_INVERSE, | ||||
|     LAST_FINITE_SET_OP | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -298,4 +298,19 @@ void finite_set_axioms::subset_axiom(expr* a) { | |||
|     clause2.push_back(a); | ||||
|     clause2.push_back(m.mk_not(eq)); | ||||
|     m_add_clause(clause2); | ||||
| } | ||||
| 
 | ||||
| void finite_set_axioms::extensionality_axiom(expr *a, expr* b) { | ||||
|     // a != b => set.in (set.diff(a, b) a) != set.in (set.diff(a, b) b)
 | ||||
|     expr_ref diff_ab(u.mk_difference(a, b), m); | ||||
| 
 | ||||
|     expr_ref a_eq_b(m.mk_eq(a, b), m); | ||||
|     expr_ref diff_in_a(u.mk_in(diff_ab, a), m); | ||||
|     expr_ref diff_in_b(u.mk_in(diff_ab, b), m); | ||||
|      | ||||
|     // (a != b) => (x in diff_ab != x in diff_ba)
 | ||||
|     expr_ref_vector clause(m); | ||||
|     clause.push_back(a_eq_b); | ||||
|     clause.push_back(m.mk_not(m.mk_iff(diff_in_a, diff_in_b))); | ||||
|     m_add_clause(clause);    | ||||
| } | ||||
|  | @ -69,4 +69,7 @@ public: | |||
|     // set.size(a) = 1
 | ||||
|     void size_singleton_axiom(expr *a); | ||||
| 
 | ||||
|     // a != b => set.in (set.diff(a, b) a) != set.in (set.diff(a, b) b)
 | ||||
|     void extensionality_axiom(expr *a, expr *b); | ||||
| 
 | ||||
| }; | ||||
|  | @ -131,9 +131,18 @@ namespace smt { | |||
|     } | ||||
| 
 | ||||
|     void theory_finite_set::new_diseq_eh(theory_var v1, theory_var v2) { | ||||
|         TRACE(finite_set, tout << "new_diseq_eh: v" << v1 << " != v" << v2 << "\n";); | ||||
|         // Disequalities could trigger extensionality axioms
 | ||||
|         // For now, we rely on the final_check to handle this
 | ||||
|         TRACE(finite_set, tout << "new_diseq_eh: v" << v1 << " != v" << v2 << "\n"); | ||||
|         auto n1 = get_enode(v1); | ||||
|         auto n2 = get_enode(v2); | ||||
|         auto e1 = n1->get_expr(); | ||||
|         auto e2 = n2->get_expr(); | ||||
|         if (u.is_finite_set(e1) && u.is_finite_set(e2)) { | ||||
|             if (e1->get_id() > e2->get_id()) | ||||
|                 std::swap(e1, e2); | ||||
|             if (!is_new_axiom(e1, e2)) | ||||
|                 return; | ||||
|             m_axioms.extensionality_axiom(e1, e2); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -143,7 +152,7 @@ namespace smt { | |||
|      *  | ||||
|      * It ensures saturation with respect to the theory axioms: | ||||
|      * - membership axioms | ||||
|      * - extensionality axioms | ||||
|      * - assume eqs axioms | ||||
|     */ | ||||
|     final_check_status theory_finite_set::final_check_eh() { | ||||
|         TRACE(finite_set, tout << "final_check_eh\n";); | ||||
|  | @ -151,7 +160,7 @@ namespace smt { | |||
|         if (add_membership_axioms()) | ||||
|             return FC_CONTINUE; | ||||
| 
 | ||||
|         if (add_extensionality_axioms()) | ||||
|         if (assume_eqs()) | ||||
|             return FC_CONTINUE; | ||||
|          | ||||
|         return FC_DONE; | ||||
|  | @ -219,32 +228,76 @@ namespace smt { | |||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      *  Saturate with respect to extensionality: | ||||
|      *  Saturate with respect to equality sharing: | ||||
|      *  - Sets corresponding to shared variables having the same interpretation should also be congruent | ||||
|     */ | ||||
|     bool theory_finite_set::add_extensionality_axioms() { | ||||
|     bool theory_finite_set::assume_eqs() { | ||||
|         collect_members(); | ||||
|         expr_ref_vector trail(m); // make sure reference counts to union expressions are valid in this scope
 | ||||
|         obj_map<expr, enode*> set_reprs; | ||||
| 
 | ||||
|         auto start = ctx.get_random_value(); | ||||
|         auto sz = get_num_vars(); | ||||
|         for (unsigned w = 0; w < sz; ++w) { | ||||
|             auto v = (w + start) % sz; | ||||
|             enode* n = get_enode(v); | ||||
|             if (!u.is_finite_set(n->get_expr())) | ||||
|                 continue; | ||||
|             if (!is_relevant_and_shared(n)) | ||||
|                 continue; | ||||
|             auto r = n->get_root(); | ||||
|             // Create a union expression that is canonical (sorted)
 | ||||
|             auto& set = *m_set_members[r]; | ||||
|             ptr_vector<expr> elems; | ||||
|             for (auto e : set) | ||||
|                 elems.push_back(e->get_expr()); | ||||
|             std::sort(elems.begin(), elems.end(), [](expr *a, expr *b) { return a->get_id() < b->get_id(); }); | ||||
|             expr* s = nullptr; | ||||
|             for (auto v : elems) | ||||
|                 s = s ? u.mk_union(s, v) : v; | ||||
|             trail.push_back(s); | ||||
|             enode *n2 = nullptr; | ||||
|             if (!set_reprs.find(s, n2)) { | ||||
|                 set_reprs.insert(s, n2); | ||||
|                 continue; | ||||
|             } | ||||
|             if (n2->get_root() == r) | ||||
|                 continue; | ||||
|             if (is_new_axiom(n->get_expr(), n2->get_expr()) && assume_eq(n, n2)) { | ||||
|                 TRACE(finite_set, | ||||
|                       tout << "assume " << mk_pp(n->get_expr(), m) << " = " << mk_pp(n2->get_expr(), m) << "\n";); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool theory_finite_set::is_new_axiom(expr* a, expr* b) { | ||||
|         struct insert_obj_pair_table : public trail { | ||||
|             obj_pair_hashtable<expr, expr> &table; | ||||
|             expr *a, *b; | ||||
|             insert_obj_pair_table(obj_pair_hashtable<expr, expr> &t, expr *a, expr *b) : table(t), a(a), b(b) {} | ||||
|             void undo() override { | ||||
|                 table.erase({a, b}); | ||||
|             } | ||||
|         }; | ||||
|         if (m_lemma_exprs.contains({a, b})) | ||||
|             return false; | ||||
|         m_lemma_exprs.insert({a, b}); | ||||
|         ctx.push_trail(insert_obj_pair_table(m_lemma_exprs, a, b)); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * Instantiate axioms for a given element in a set. | ||||
|     */ | ||||
|     void theory_finite_set::add_membership_axioms(expr *elem, expr *set) { | ||||
|         TRACE(finite_set, tout << "add_membership_axioms: " << mk_pp(elem, m) << " in " << mk_pp(set, m) << "\n";); | ||||
| 
 | ||||
|         struct insert_obj_pair_table : public trail { | ||||
|             obj_pair_hashtable<expr, expr> &table; | ||||
|             expr *a, *b; | ||||
|             insert_obj_pair_table(obj_pair_hashtable<expr, expr> &t, expr *a, expr *b) :  | ||||
|                 table(t), a(a), b(b) {} | ||||
|             void undo() override { | ||||
|                 table.erase({a, b}); | ||||
|             } | ||||
|         }; | ||||
|         if (m_lemma_exprs.contains({elem, set})) | ||||
|         if (!is_new_axiom(elem, set)) | ||||
|             return; | ||||
|         m_lemma_exprs.insert({elem, set}); | ||||
|         ctx.push_trail(insert_obj_pair_table(m_lemma_exprs, elem, set)); | ||||
| 
 | ||||
|         // Instantiate appropriate axiom based on set structure
 | ||||
|         if (u.is_empty(set)) { | ||||
|             m_axioms.in_empty_axiom(elem); | ||||
|  | @ -296,8 +349,6 @@ namespace smt { | |||
|         collect_members(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     void theory_finite_set::collect_members() { | ||||
|         // This method can be used to collect all elements that are members of sets
 | ||||
|         // and ensure that the model factory has values for them.
 | ||||
|  |  | |||
|  | @ -128,7 +128,8 @@ namespace smt { | |||
|         lbool truth_value(expr *e); | ||||
|         void add_immediate_axioms(app *atom); | ||||
|         bool add_membership_axioms(); | ||||
|         bool add_extensionality_axioms(); | ||||
|         bool assume_eqs(); | ||||
|         bool is_new_axiom(expr *a, expr *b); | ||||
| 
 | ||||
|         // model construction
 | ||||
|         void collect_members(); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue