mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	Complete theory_finite_set.h header and implement finite set theory solver (#7976)
* Initial plan * Implement theory_finite_set header and implementation Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add theory registration to smt_setup Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Update theory_finite_set.cpp * Refactor membership_atoms and add elements list Renamed membership_atoms to membership_elements and added elements list. * Change membership elements to use enode type * Update theory_finite_set.cpp * Fix typo in internalize_atom function * Update theory_finite_set.cpp * Refactor final_check_eh by removing comments Removed redundant comments and cleaned up code. * Add m_lemmas member to theory_finite_set class * Improve clause management and instantiation logic Refactor clause handling and instantiate logic in finite set theory. * Add friend class declaration for testing * Add placeholder methods for lemma instantiation Added placeholder methods for lemma instantiation. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									7446112fbe
								
							
						
					
					
						commit
						b4d41ffe81
					
				
					 5 changed files with 248 additions and 2 deletions
				
			
		|  | @ -57,6 +57,7 @@ z3_add_component(smt | ||||||
|     theory_char.cpp |     theory_char.cpp | ||||||
|     theory_datatype.cpp |     theory_datatype.cpp | ||||||
|     theory_dense_diff_logic.cpp |     theory_dense_diff_logic.cpp | ||||||
|  |     theory_finite_set.cpp | ||||||
|     theory_diff_logic.cpp |     theory_diff_logic.cpp | ||||||
|     theory_dl.cpp |     theory_dl.cpp | ||||||
|     theory_dummy.cpp |     theory_dummy.cpp | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ Revision History: | ||||||
| #include "smt/theory_pb.h" | #include "smt/theory_pb.h" | ||||||
| #include "smt/theory_fpa.h" | #include "smt/theory_fpa.h" | ||||||
| #include "smt/theory_polymorphism.h" | #include "smt/theory_polymorphism.h" | ||||||
|  | #include "smt/theory_finite_set.h" | ||||||
| 
 | 
 | ||||||
| namespace smt { | namespace smt { | ||||||
| 
 | 
 | ||||||
|  | @ -784,6 +785,10 @@ namespace smt { | ||||||
|         m_context.register_plugin(alloc(smt::theory_char, m_context));         |         m_context.register_plugin(alloc(smt::theory_char, m_context));         | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void setup::setup_finite_set() { | ||||||
|  |         m_context.register_plugin(alloc(smt::theory_finite_set, m_context)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void setup::setup_special_relations() { |     void setup::setup_special_relations() { | ||||||
|         m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager)); |         m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager)); | ||||||
|     } |     } | ||||||
|  | @ -807,6 +812,7 @@ namespace smt { | ||||||
|         setup_dl(); |         setup_dl(); | ||||||
|         setup_seq_str(st); |         setup_seq_str(st); | ||||||
|         setup_fpa(); |         setup_fpa(); | ||||||
|  |         setup_finite_set(); | ||||||
|         setup_special_relations(); |         setup_special_relations(); | ||||||
|         setup_polymorphism(); |         setup_polymorphism(); | ||||||
|         setup_relevancy(st); |         setup_relevancy(st); | ||||||
|  |  | ||||||
|  | @ -102,6 +102,7 @@ namespace smt { | ||||||
|         void setup_seq_str(static_features const & st); |         void setup_seq_str(static_features const & st); | ||||||
|         void setup_seq(); |         void setup_seq(); | ||||||
|         void setup_char(); |         void setup_char(); | ||||||
|  |         void setup_finite_set(); | ||||||
|         void setup_card(); |         void setup_card(); | ||||||
|         void setup_sls(); |         void setup_sls(); | ||||||
|         void setup_i_arith(); |         void setup_i_arith(); | ||||||
|  |  | ||||||
							
								
								
									
										209
									
								
								src/smt/theory_finite_set.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/smt/theory_finite_set.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2025 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     theory_finite_set.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Theory solver for finite sets. | ||||||
|  |     Implements axiom schemas for finite set operations. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     GitHub Copilot Agent 2025 | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "smt/theory_finite_set.h" | ||||||
|  | #include "smt/smt_context.h" | ||||||
|  | #include "smt/smt_model_generator.h" | ||||||
|  | #include "ast/ast_pp.h" | ||||||
|  | 
 | ||||||
|  | namespace smt { | ||||||
|  | 
 | ||||||
|  |     theory_finite_set::theory_finite_set(context& ctx): | ||||||
|  |         theory(ctx, ctx.get_manager().mk_family_id("finite_set")), | ||||||
|  |         u(m), | ||||||
|  |         m_axioms(m) | ||||||
|  |     { | ||||||
|  |         // Setup the add_clause callback for axioms
 | ||||||
|  |         std::function<void(expr_ref_vector const &)> add_clause_fn =  | ||||||
|  |             [this](expr_ref_vector const& clause) { | ||||||
|  |                 this->m_lemmas.push_back(clause); | ||||||
|  |             }; | ||||||
|  |         m_axioms.set_add_clause(add_clause_fn); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool theory_finite_set::internalize_atom(app * atom, bool gate_ctx) { | ||||||
|  |         TRACE("finite_set", tout << "internalize_atom: " << mk_pp(atom, m) << "\n";); | ||||||
|  | 
 | ||||||
|  |         internalize_term(atom); | ||||||
|  |          | ||||||
|  |         // Track membership elements (set.in)
 | ||||||
|  |         expr* elem = nullptr, *set = nullptr; | ||||||
|  |         if (u.is_in(atom, elem, set)) { | ||||||
|  |             auto n = ctx.get_enode(elem); | ||||||
|  |             if (!m_elements.contains(n)) { | ||||||
|  |                 m_elements.insert(n); | ||||||
|  |                 ctx.push_trail(insert_obj_trail(n)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool theory_finite_set::internalize_term(app * term) { | ||||||
|  |         TRACE("finite_set", tout << "internalize_term: " << mk_pp(term, m) << "\n";); | ||||||
|  |          | ||||||
|  |         // Internalize all arguments first
 | ||||||
|  |         for (expr* arg : *term)  | ||||||
|  |             ctx.internalize(arg, false); | ||||||
|  |          | ||||||
|  |         // Create boolean variable for Boolean terms
 | ||||||
|  |         if (m.is_bool(term) && !ctx.b_internalized(term)) { | ||||||
|  |             bool_var bv = ctx.mk_bool_var(term); | ||||||
|  |             ctx.set_var_theory(bv, get_id()); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Create enode for the term if needed
 | ||||||
|  |         enode* e = nullptr; | ||||||
|  |         if (ctx.e_internalized(term))  | ||||||
|  |             e = ctx.get_enode(term);          | ||||||
|  |         else  | ||||||
|  |             e = ctx.mk_enode(term, false, m.is_bool(term), true);         | ||||||
|  |          | ||||||
|  |         // Attach theory variable if this is a set
 | ||||||
|  |         if (!is_attached_to_var(e))              | ||||||
|  |             ctx.attach_th_var(e, this, mk_var(e)); | ||||||
|  |                  | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::new_eq_eh(theory_var v1, theory_var v2) { | ||||||
|  |         TRACE("finite_set", tout << "new_eq_eh: v" << v1 << " = v" << v2 << "\n";); | ||||||
|  |         // When two sets are equal, propagate membership constraints
 | ||||||
|  |         // This is handled by congruence closure, so no additional work needed here
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     final_check_status theory_finite_set::final_check_eh() { | ||||||
|  |         TRACE("finite_set", tout << "final_check_eh\n";); | ||||||
|  | 
 | ||||||
|  |         // walk all parents of elem in congruence table.
 | ||||||
|  |         // if a parent is of the form elem' in S u T, or similar.
 | ||||||
|  |         // create clauses for elem in S u T.
 | ||||||
|  | 
 | ||||||
|  |         expr* elem1 = nullptr, *set1 = nullptr; | ||||||
|  |         m_lemmas.reset(); | ||||||
|  |         for (auto elem : m_elements) { | ||||||
|  |             for (auto p : enode::parents(elem)) { | ||||||
|  |                 if (!u.is_in(p->get_expr(), elem1, set1))  | ||||||
|  |                     continue; | ||||||
|  |                 if (elem->get_root() != p->get_arg(0)->get_root())                     | ||||||
|  |                     continue; // elem is then equal to set1 but not elem1. This is a different case.
 | ||||||
|  |                 for (auto sib : *p->get_arg(1)) | ||||||
|  |                     instantiate_axioms(elem->get_expr(), sib->get_expr()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (instantiate_false_lemma()) | ||||||
|  |             return FC_CONTINUE; | ||||||
|  |         if (instantiate_unit_propagation()) | ||||||
|  |             return FC_CONTINUE; | ||||||
|  |         if (instantiate_free_lemma()) | ||||||
|  |             return FC_CONTINUE; | ||||||
|  |          | ||||||
|  |         return FC_DONE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::instantiate_axioms(expr* elem, expr* set) { | ||||||
|  |         TRACE("finite_set", tout << "instantiate_axioms: " << mk_pp(elem, m) << " in " << mk_pp(set, m) << "\n";); | ||||||
|  |          | ||||||
|  |         // Instantiate appropriate axiom based on set structure
 | ||||||
|  |         if (u.is_empty(set)) { | ||||||
|  |             m_axioms.in_empty_axiom(elem); | ||||||
|  |         } | ||||||
|  |         else if (u.is_singleton(set)) { | ||||||
|  |             m_axioms.in_singleton_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |         else if (u.is_union(set)) { | ||||||
|  |             m_axioms.in_union_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |         else if (u.is_intersect(set)) { | ||||||
|  |             m_axioms.in_intersect_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |         else if (u.is_difference(set)) { | ||||||
|  |             m_axioms.in_difference_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |         else if (u.is_range(set)) { | ||||||
|  |             m_axioms.in_range_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |         else if (u.is_map(set)) { | ||||||
|  |             m_axioms.in_map_axiom(elem, set); | ||||||
|  |             m_axioms.in_map_image_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |         else if (u.is_select(set)) { | ||||||
|  |             m_axioms.in_select_axiom(elem, set); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // Instantiate size axioms for singleton sets
 | ||||||
|  |         // TODO, such axioms don't belong here
 | ||||||
|  |         if (u.is_singleton(set)) { | ||||||
|  |             m_axioms.size_singleton_axiom(set); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::add_clause(expr_ref_vector const& clause) { | ||||||
|  |         TRACE("finite_set",  | ||||||
|  |             tout << "add_clause: " << clause << "\n"); | ||||||
|  |          | ||||||
|  |         // Convert expressions to literals and assert the clause
 | ||||||
|  |         literal_vector lits; | ||||||
|  |         for (expr* e : clause) { | ||||||
|  |             ctx.internalize(e, false); | ||||||
|  |             literal lit = ctx.get_literal(lit_expr); | ||||||
|  |             lits.push_back(lit); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (!lits.empty()) { | ||||||
|  |             scoped_trace_stream _sts(*this, lits); | ||||||
|  |             ctx.mk_th_axiom(get_id(), lits); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     theory * theory_finite_set::mk_fresh(context * new_ctx) { | ||||||
|  |         return alloc(theory_finite_set, *new_ctx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::display(std::ostream & out) const { | ||||||
|  |         out << "theory_finite_set:\n"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::init_model(model_generator & mg) { | ||||||
|  |         TRACE("finite_set", tout << "init_model\n";); | ||||||
|  |         // Model generation will use default interpretation for sets
 | ||||||
|  |         // The model will be constructed based on the membership literals that are true
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     model_value_proc * theory_finite_set::mk_value(enode * n, model_generator & mg) { | ||||||
|  |         TRACE("finite_set", tout << "mk_value: " << mk_pp(n->get_expr(), m) << "\n";); | ||||||
|  |          | ||||||
|  |         // For now, return nullptr to use default model construction
 | ||||||
|  |         // A complete implementation would construct explicit set values
 | ||||||
|  |         // based on true membership literals
 | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::instantiate_false_lemma() {} | ||||||
|  |     void theory_finite_set::instantiate_unit_propagation() {} | ||||||
|  |     void theory_finite_set::instantiate_free_lemma() {} | ||||||
|  | 
 | ||||||
|  | }  // namespace smt
 | ||||||
|  | @ -86,13 +86,42 @@ theory_finite_set.cpp. | ||||||
| 
 | 
 | ||||||
| #include "ast/ast.h" | #include "ast/ast.h" | ||||||
| #include "ast/ast_pp.h" | #include "ast/ast_pp.h" | ||||||
|  | #include "ast/finite_set_decl_plugin.h" | ||||||
|  | #include "ast/rewriter/finite_set_axioms.h" | ||||||
| #include "smt/smt_theory.h" | #include "smt/smt_theory.h" | ||||||
| 
 | 
 | ||||||
| namespace smt { | namespace smt { | ||||||
|     class theory_finite_set : public theory { |     class theory_finite_set : public theory { | ||||||
|  |         friend class theory_finite_set_test; | ||||||
|  |         finite_set_util           u; | ||||||
|  |         finite_set_axioms         m_axioms; | ||||||
|  |         obj_hashtable<enode>      m_elements;             // set of all 'x' where there is an 'x in S' atom
 | ||||||
|  |         vector<expr_ref_vector>   m_lemmas; | ||||||
|  |          | ||||||
|  |     protected: | ||||||
|  |         // Override relevant methods from smt::theory
 | ||||||
|  |         bool internalize_atom(app * atom, bool gate_ctx) override; | ||||||
|  |         bool internalize_term(app * term) override; | ||||||
|  |         void new_eq_eh(theory_var v1, theory_var v2) override; | ||||||
|  |         void new_diseq_eh(theory_var v1, theory_var v2) override; | ||||||
|  |         final_check_status final_check_eh() override; | ||||||
|  |          | ||||||
|  |         theory * mk_fresh(context * new_ctx) override; | ||||||
|  |         char const * get_name() const override { return "finite_set"; } | ||||||
|  |         void display(std::ostream & out) const override; | ||||||
|  |         void init_model(model_generator & mg) override; | ||||||
|  |         model_value_proc * mk_value(enode * n, model_generator & mg) override; | ||||||
|  | 
 | ||||||
|  |         // Helper methods for axiom instantiation
 | ||||||
|  |         void instantiate_axioms(expr* elem, expr* set); | ||||||
|  |         void add_clause(expr_ref_vector const& clause); | ||||||
|  |         void instantiate_false_lemma(); | ||||||
|  |         void instantiate_unit_propagation(); | ||||||
|  |         void instantiate_free_lemma(); | ||||||
|  |          | ||||||
|     public: |     public: | ||||||
|         theory_finite_set(ast_manager & m); |         theory_finite_set(context& ctx); | ||||||
|         ~theory_finite_set() override {} |         ~theory_finite_set() override {} | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| }  // namespace smt 
 | }  // namespace smt 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue