mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 19:52:29 +00:00 
			
		
		
		
	Implement finite_set_decl_plugin with complete operator support and polymorphism infrastructure (#7961)
* Initial plan * Implement finite_sets_decl_plugin with all specified operations Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add tests for finite_sets_decl_plugin Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add set.singleton operator to finite_sets_decl_plugin Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Refactor finite_sets_decl_plugin to use polymorphic signatures and Array sorts Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Rename finite_sets to finite_set everywhere including file names Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Rename set.filter to set.select Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Refactor finite_set_decl_plugin to use polymorphism_util Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Move psig and match method to polymorphism_util Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add MATCH macros and fix is_fully_interp return value Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Add is_finite_set helper and parameter count validation Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									7356b5ff88
								
							
						
					
					
						commit
						df4052ec69
					
				
					 9 changed files with 572 additions and 0 deletions
				
			
		|  | @ -28,6 +28,7 @@ z3_add_component(ast | |||
|     expr_map.cpp | ||||
|     expr_stat.cpp | ||||
|     expr_substitution.cpp | ||||
|     finite_set_decl_plugin.cpp | ||||
|     for_each_ast.cpp | ||||
|     for_each_expr.cpp | ||||
|     format.cpp | ||||
|  |  | |||
							
								
								
									
										192
									
								
								src/ast/finite_set_decl_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/ast/finite_set_decl_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,192 @@ | |||
| /*++
 | ||||
| Copyright (c) 2025 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     finite_set_decl_plugin.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Declaration plugin for finite sets | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     GitHub Copilot Agent 2025 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| #include<sstream> | ||||
| #include "ast/finite_set_decl_plugin.h" | ||||
| #include "ast/arith_decl_plugin.h" | ||||
| #include "ast/array_decl_plugin.h" | ||||
| #include "ast/polymorphism_util.h" | ||||
| #include "util/warning.h" | ||||
| 
 | ||||
| finite_set_decl_plugin::finite_set_decl_plugin(): | ||||
|     m_init(false) { | ||||
| } | ||||
| 
 | ||||
| finite_set_decl_plugin::~finite_set_decl_plugin() { | ||||
|     for (polymorphism::psig* s : m_sigs)  | ||||
|         dealloc(s); | ||||
| } | ||||
| 
 | ||||
| void finite_set_decl_plugin::init() { | ||||
|     if (m_init) return; | ||||
|     ast_manager& m = *m_manager; | ||||
|     array_util autil(m); | ||||
|     m_init = true; | ||||
|      | ||||
|     sort* A = m.mk_type_var(symbol("A")); | ||||
|     sort* B = m.mk_type_var(symbol("B")); | ||||
|     parameter paramA(A); | ||||
|     parameter paramB(B); | ||||
|     sort* setA = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, ¶mA); | ||||
|     sort* setB = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, ¶mB); | ||||
|     sort* boolT = m.mk_bool_sort(); | ||||
|     sort* intT = arith_util(m).mk_int(); | ||||
|     parameter paramInt(intT); | ||||
|     sort* setInt = m.mk_sort(m_family_id, FINITE_SET_SORT, 1, ¶mInt); | ||||
|     sort* arrAB = autil.mk_array_sort(A, B); | ||||
|     sort* arrABool = autil.mk_array_sort(A, boolT); | ||||
|      | ||||
|     sort* setAsetA[2] = { setA, setA }; | ||||
|     sort* AsetA[2] = { A, setA }; | ||||
|     sort* arrABsetA[2] = { arrAB, setA }; | ||||
|     sort* arrABoolsetA[2] = { arrABool, setA }; | ||||
|     sort* intintT[2] = { intT, intT }; | ||||
|      | ||||
|     m_sigs.resize(LAST_FINITE_SET_OP); | ||||
|     m_sigs[OP_FINITE_SET_EMPTY]      = alloc(polymorphism::psig, m, "set.empty",      1, 0, nullptr, setA); | ||||
|     m_sigs[OP_FINITE_SET_SINGLETON]  = alloc(polymorphism::psig, m, "set.singleton",  1, 1, &A, setA); | ||||
|     m_sigs[OP_FINITE_SET_UNION]      = alloc(polymorphism::psig, m, "set.union",      1, 2, setAsetA, setA); | ||||
|     m_sigs[OP_FINITE_SET_INTERSECT]  = alloc(polymorphism::psig, m, "set.intersect",  1, 2, setAsetA, setA); | ||||
|     m_sigs[OP_FINITE_SET_DIFFERENCE]  = alloc(polymorphism::psig, m, "set.difference", 1, 2, setAsetA, setA); | ||||
|     m_sigs[OP_FINITE_SET_IN]         = alloc(polymorphism::psig, m, "set.in",         1, 2, AsetA, boolT); | ||||
|     m_sigs[OP_FINITE_SET_SIZE]       = alloc(polymorphism::psig, m, "set.size",       1, 1, &setA, intT); | ||||
|     m_sigs[OP_FINITE_SET_SUBSET]     = alloc(polymorphism::psig, m, "set.subset",     1, 2, setAsetA, boolT); | ||||
|     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); | ||||
| } | ||||
| 
 | ||||
| sort * finite_set_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { | ||||
|     if (k == FINITE_SET_SORT) { | ||||
|         if (num_parameters != 1) { | ||||
|             m_manager->raise_exception("FiniteSet sort expects exactly one parameter (element sort)"); | ||||
|             return nullptr; | ||||
|         } | ||||
|         if (!parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { | ||||
|             m_manager->raise_exception("FiniteSet sort parameter must be a sort"); | ||||
|             return nullptr; | ||||
|         } | ||||
|         sort * element_sort = to_sort(parameters[0].get_ast()); | ||||
|         sort_size sz = sort_size::mk_very_big(); | ||||
|         sort_info info(m_family_id, FINITE_SET_SORT, sz, num_parameters, parameters); | ||||
|         return m_manager->mk_sort(symbol("FiniteSet"), info); | ||||
|     } | ||||
|     m_manager->raise_exception("unknown finite set sort"); | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| bool finite_set_decl_plugin::is_finite_set(sort* s) const { | ||||
|     return s->get_family_id() == m_family_id && s->get_decl_kind() == FINITE_SET_SORT; | ||||
| } | ||||
| 
 | ||||
| sort * finite_set_decl_plugin::get_element_sort(sort* finite_set_sort) const { | ||||
|     if (!is_finite_set(finite_set_sort)) { | ||||
|         return nullptr; | ||||
|     } | ||||
|     if (finite_set_sort->get_num_parameters() != 1) { | ||||
|         return nullptr; | ||||
|     } | ||||
|     parameter const* params = finite_set_sort->get_parameters(); | ||||
|     if (!params[0].is_ast() || !is_sort(params[0].get_ast())) { | ||||
|         return nullptr; | ||||
|     } | ||||
|     return to_sort(params[0].get_ast()); | ||||
| } | ||||
| 
 | ||||
| func_decl * finite_set_decl_plugin::mk_empty(sort* element_sort) { | ||||
|     parameter param(element_sort); | ||||
|     sort * finite_set_sort = m_manager->mk_sort(m_family_id, FINITE_SET_SORT, 1, ¶m); | ||||
|     sort * const * no_domain = nullptr; | ||||
|     return m_manager->mk_func_decl(m_sigs[OP_FINITE_SET_EMPTY]->m_name, 0u, no_domain, finite_set_sort, | ||||
|                                    func_decl_info(m_family_id, OP_FINITE_SET_EMPTY, 1, ¶m)); | ||||
| } | ||||
| 
 | ||||
| func_decl * finite_set_decl_plugin::mk_finite_set_op(decl_kind k, unsigned arity, sort * const * domain, sort* range) { | ||||
|     ast_manager& m = *m_manager; | ||||
|     polymorphism::util poly_util(m); | ||||
|     sort_ref rng(m); | ||||
|     poly_util.match(*m_sigs[k], arity, domain, range, rng); | ||||
|     return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); | ||||
| } | ||||
| 
 | ||||
| func_decl * finite_set_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters,  | ||||
|                                                    parameter const * parameters, | ||||
|                                                    unsigned arity, sort * const * domain,  | ||||
|                                                    sort * range) { | ||||
|     init(); | ||||
|      | ||||
|     switch (k) { | ||||
|     case OP_FINITE_SET_EMPTY: | ||||
|         if (num_parameters != 1 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) { | ||||
|             m_manager->raise_exception("set.empty requires one sort parameter"); | ||||
|             return nullptr; | ||||
|         } | ||||
|         return mk_empty(to_sort(parameters[0].get_ast())); | ||||
|     case OP_FINITE_SET_SINGLETON: | ||||
|     case OP_FINITE_SET_UNION: | ||||
|     case OP_FINITE_SET_INTERSECT: | ||||
|     case OP_FINITE_SET_DIFFERENCE: | ||||
|     case OP_FINITE_SET_IN: | ||||
|     case OP_FINITE_SET_SIZE: | ||||
|     case OP_FINITE_SET_SUBSET: | ||||
|     case OP_FINITE_SET_MAP: | ||||
|     case OP_FINITE_SET_SELECT: | ||||
|     case OP_FINITE_SET_RANGE: | ||||
|         return mk_finite_set_op(k, arity, domain, range); | ||||
|     default: | ||||
|         return nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void finite_set_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol const & logic) { | ||||
|     init(); | ||||
|     for (unsigned i = 0; i < m_sigs.size(); ++i) { | ||||
|         if (m_sigs[i]) | ||||
|             op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void finite_set_decl_plugin::get_sort_names(svector<builtin_name>& sort_names, symbol const & logic) { | ||||
|     sort_names.push_back(builtin_name("FiniteSet", FINITE_SET_SORT)); | ||||
| } | ||||
| 
 | ||||
| expr * finite_set_decl_plugin::get_some_value(sort * s) { | ||||
|     if (is_finite_set(s)) { | ||||
|         // Return empty set for the given sort
 | ||||
|         sort* element_sort = get_element_sort(s); | ||||
|         if (element_sort) { | ||||
|             parameter param(element_sort); | ||||
|             return m_manager->mk_app(m_family_id, OP_FINITE_SET_EMPTY, 1, ¶m, 0, nullptr); | ||||
|         } | ||||
|     } | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| bool finite_set_decl_plugin::is_fully_interp(sort * s) const { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool finite_set_decl_plugin::is_value(app * e) const { | ||||
|     // Empty set is a value
 | ||||
|     return is_app_of(e, m_family_id, OP_FINITE_SET_EMPTY); | ||||
| } | ||||
| 
 | ||||
| bool finite_set_decl_plugin::is_unique_value(app* e) const { | ||||
|     // Empty set is a unique value for its sort
 | ||||
|     return is_value(e); | ||||
| } | ||||
							
								
								
									
										188
									
								
								src/ast/finite_set_decl_plugin.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src/ast/finite_set_decl_plugin.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,188 @@ | |||
| /*++
 | ||||
| Copyright (c) 2025 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     finite_set_decl_plugin.h | ||||
| 
 | ||||
| Abstract: | ||||
|     Declaration plugin for finite sets signatures | ||||
| 
 | ||||
| Sort: | ||||
|     FiniteSet(S) | ||||
| 
 | ||||
| Operators: | ||||
|     set.empty : (FiniteSet S) | ||||
|     set.singleton : S -> (FiniteSet S) | ||||
|     set.union : (FiniteSet S) (FiniteSet S) -> (FiniteSet S) | ||||
|     set.intersect : (FiniteSet S) (FiniteSet S) -> (FiniteSet S) | ||||
|     set.difference : (FiniteSet S) (FiniteSet S) -> (FiniteSet S) | ||||
|     set.in : S (FiniteSet S) -> Bool | ||||
|     set.size : (FiniteSet S) -> Int | ||||
|     set.subset : (FiniteSet S) (FiniteSet S) -> Bool | ||||
|     set.map : (S -> T) (FiniteSet S) -> (FiniteSet T) | ||||
|     set.select : (S -> Bool) (FiniteSet S) -> (FiniteSet S) | ||||
|     set.range : Int Int -> (FiniteSet Int) | ||||
|     | ||||
| --*/ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast/ast.h" | ||||
| #include "ast/polymorphism_util.h" | ||||
| 
 | ||||
| enum finite_set_sort_kind { | ||||
|     FINITE_SET_SORT | ||||
| }; | ||||
| 
 | ||||
| enum finite_set_op_kind { | ||||
|     OP_FINITE_SET_EMPTY, | ||||
|     OP_FINITE_SET_SINGLETON, | ||||
|     OP_FINITE_SET_UNION, | ||||
|     OP_FINITE_SET_INTERSECT, | ||||
|     OP_FINITE_SET_DIFFERENCE, | ||||
|     OP_FINITE_SET_IN, | ||||
|     OP_FINITE_SET_SIZE, | ||||
|     OP_FINITE_SET_SUBSET, | ||||
|     OP_FINITE_SET_MAP, | ||||
|     OP_FINITE_SET_SELECT, | ||||
|     OP_FINITE_SET_RANGE, | ||||
|     LAST_FINITE_SET_OP | ||||
| }; | ||||
| 
 | ||||
| class finite_set_decl_plugin : public decl_plugin { | ||||
|     ptr_vector<polymorphism::psig>   m_sigs; | ||||
|     bool                             m_init; | ||||
| 
 | ||||
|     void init(); | ||||
|     func_decl * mk_empty(sort* element_sort); | ||||
|     func_decl * mk_finite_set_op(decl_kind k, unsigned arity, sort * const * domain, sort* range); | ||||
|     sort * get_element_sort(sort* finite_set_sort) const; | ||||
|     bool is_finite_set(sort* s) const; | ||||
| 
 | ||||
| public: | ||||
|     finite_set_decl_plugin(); | ||||
|     ~finite_set_decl_plugin() override; | ||||
| 
 | ||||
|     decl_plugin * mk_fresh() override { | ||||
|         return alloc(finite_set_decl_plugin); | ||||
|     } | ||||
|      | ||||
|     void finalize() override { | ||||
|         for (polymorphism::psig* s : m_sigs)  | ||||
|             dealloc(s); | ||||
|         m_sigs.reset(); | ||||
|     } | ||||
| 
 | ||||
|     //
 | ||||
|     // Contract for sort:
 | ||||
|     //   parameters[0]     - element sort
 | ||||
|     //
 | ||||
|     sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; | ||||
| 
 | ||||
|     //
 | ||||
|     // Contract for func_decl:
 | ||||
|     //   For OP_FINITE_SET_MAP and OP_FINITE_SET_FILTER:
 | ||||
|     //     parameters[0]     - function declaration
 | ||||
|     //   For others:
 | ||||
|     //     no parameters
 | ||||
|     func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, | ||||
|                              unsigned arity, sort * const * domain, sort * range) override; | ||||
| 
 | ||||
|     void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override; | ||||
| 
 | ||||
|     void get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) override; | ||||
| 
 | ||||
|     expr * get_some_value(sort * s) override; | ||||
| 
 | ||||
|     bool is_fully_interp(sort * s) const override; | ||||
| 
 | ||||
|     bool is_value(app * e) const override; | ||||
| 
 | ||||
|     bool is_unique_value(app* e) const override; | ||||
| }; | ||||
| 
 | ||||
| class finite_set_recognizers { | ||||
| protected: | ||||
|     family_id m_fid; | ||||
| public: | ||||
|     finite_set_recognizers(family_id fid):m_fid(fid) {} | ||||
|     family_id get_family_id() const { return m_fid; } | ||||
|     bool is_finite_set(sort* s) const { return is_sort_of(s, m_fid, FINITE_SET_SORT); } | ||||
|     bool is_finite_set(expr const* n) const { return is_finite_set(n->get_sort()); } | ||||
|     bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_EMPTY); } | ||||
|     bool is_singleton(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SINGLETON); } | ||||
|     bool is_union(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_UNION); } | ||||
|     bool is_intersect(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_INTERSECT); } | ||||
|     bool is_difference(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_DIFFERENCE); } | ||||
|     bool is_in(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_IN); } | ||||
|     bool is_size(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SIZE); } | ||||
|     bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SUBSET); } | ||||
|     bool is_map(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_MAP); } | ||||
|     bool is_select(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_SELECT); } | ||||
|     bool is_range(expr const* n) const { return is_app_of(n, m_fid, OP_FINITE_SET_RANGE); } | ||||
| 
 | ||||
|     MATCH_UNARY(is_singleton); | ||||
|     MATCH_UNARY(is_size); | ||||
|     MATCH_BINARY(is_union); | ||||
|     MATCH_BINARY(is_intersect); | ||||
|     MATCH_BINARY(is_difference); | ||||
|     MATCH_BINARY(is_in); | ||||
|     MATCH_BINARY(is_subset); | ||||
|     MATCH_BINARY(is_map); | ||||
|     MATCH_BINARY(is_select); | ||||
|     MATCH_BINARY(is_range); | ||||
| }; | ||||
| 
 | ||||
| class finite_set_util : public finite_set_recognizers { | ||||
|     ast_manager& m_manager; | ||||
| public: | ||||
|     finite_set_util(ast_manager& m): | ||||
|         finite_set_recognizers(m.mk_family_id("finite_set")), m_manager(m) {} | ||||
|      | ||||
|     ast_manager& get_manager() const { return m_manager; } | ||||
| 
 | ||||
|     app * mk_empty(sort* element_sort) { | ||||
|         parameter param(element_sort); | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_EMPTY, 1, ¶m, 0, nullptr); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_singleton(expr* elem) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_SINGLETON, elem); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_union(expr* s1, expr* s2) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_UNION, s1, s2); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_intersect(expr* s1, expr* s2) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_INTERSECT, s1, s2); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_difference(expr* s1, expr* s2) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_DIFFERENCE, s1, s2); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_in(expr* elem, expr* set) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_IN, elem, set); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_size(expr* set) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_SIZE, set); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_subset(expr* s1, expr* s2) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_SUBSET, s1, s2); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_map(expr* arr, expr* set) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_MAP, arr, set); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_select(expr* arr, expr* set) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_SELECT, arr, set); | ||||
|     } | ||||
| 
 | ||||
|     app * mk_range(expr* low, expr* high) { | ||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_RANGE, low, high); | ||||
|     } | ||||
| }; | ||||
|  | @ -350,4 +350,31 @@ namespace polymorphism { | |||
|         proc proc(m, tvs); | ||||
|         for_each_ast(proc, e, true); | ||||
|     } | ||||
| 
 | ||||
|     void util::match(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& range_out) { | ||||
|         if (dsz != sig.m_dom.size()) { | ||||
|             std::ostringstream strm; | ||||
|             strm << "Incorrect number of arguments to '" << sig.m_name << "' "; | ||||
|             strm << "expected " << sig.m_dom.size() << " given " << dsz; | ||||
|             m.raise_exception(strm.str()); | ||||
|         } | ||||
|          | ||||
|         substitution sub(m); | ||||
|         bool is_match = true; | ||||
|         for (unsigned i = 0; is_match && i < dsz; ++i) { | ||||
|             SASSERT(dom[i]); | ||||
|             is_match = sub.match(sig.m_dom.get(i), dom[i]); | ||||
|         } | ||||
|         if (range && is_match) { | ||||
|             is_match = sub.match(sig.m_range, range); | ||||
|         } | ||||
|         if (!is_match) { | ||||
|             std::ostringstream strm; | ||||
|             strm << "Sort mismatch for function '" << sig.m_name << "'"; | ||||
|             m.raise_exception(strm.str()); | ||||
|         } | ||||
|          | ||||
|         // Apply substitution to get the range
 | ||||
|         range_out = sub(sig.m_range); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -78,6 +78,24 @@ namespace polymorphism { | |||
| 
 | ||||
|     typedef hashtable<substitution*, substitution::hash, substitution::eq> substitutions; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Polymorphic signature for operators | ||||
|      */ | ||||
|     struct psig { | ||||
|         symbol          m_name; | ||||
|         unsigned        m_num_params; | ||||
|         sort_ref_vector m_dom; | ||||
|         sort_ref        m_range; | ||||
|         psig(ast_manager& m, char const* name, unsigned n, unsigned dsz, sort* const* dom, sort* rng): | ||||
|             m_name(name), | ||||
|             m_num_params(n), | ||||
|             m_dom(m), | ||||
|             m_range(rng, m) | ||||
|         { | ||||
|             m_dom.append(dsz, dom); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     class util { | ||||
|         ast_manager&         m; | ||||
|         sort_ref_vector      m_trail; | ||||
|  | @ -100,6 +118,13 @@ namespace polymorphism { | |||
| 
 | ||||
|         bool match(substitution& sub, sort* s1, sort* s_ground); | ||||
| 
 | ||||
|         /**
 | ||||
|          * Match a polymorphic signature against concrete argument sorts. | ||||
|          * Raises exception if arity mismatch or type mismatch. | ||||
|          * Returns the instantiated range sort via range_out. | ||||
|          */ | ||||
|         void match(psig& sig, unsigned dsz, sort* const* dom, sort* range, sort_ref& range_out); | ||||
|                          | ||||
|         // collect instantiations of polymorphic functions
 | ||||
|         void collect_poly_instances(expr* e, ptr_vector<func_decl>& instances); | ||||
|          | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ Revision History: | |||
| #include "ast/pb_decl_plugin.h" | ||||
| #include "ast/fpa_decl_plugin.h" | ||||
| #include "ast/special_relations_decl_plugin.h" | ||||
| #include "ast/finite_set_decl_plugin.h" | ||||
| 
 | ||||
| void reg_decl_plugins(ast_manager & m) { | ||||
|     if (!m.get_plugin(m.mk_family_id(symbol("arith")))) { | ||||
|  | @ -64,4 +65,7 @@ void reg_decl_plugins(ast_manager & m) { | |||
|     if (!m.get_plugin(m.mk_family_id(symbol("specrels")))) { | ||||
|         m.register_plugin(symbol("specrels"), alloc(special_relations_decl_plugin)); | ||||
|     } | ||||
|     if (!m.get_plugin(m.mk_family_id(symbol("finite_set")))) { | ||||
|         m.register_plugin(symbol("finite_set"), alloc(finite_set_decl_plugin)); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -54,6 +54,7 @@ add_executable(test-z3 | |||
|   expr_substitution.cpp | ||||
|   ext_numeral.cpp | ||||
|   f2n.cpp | ||||
|   finite_set.cpp | ||||
|   factor_rewriter.cpp | ||||
|   finder.cpp | ||||
|   fixed_bit_vector.cpp | ||||
|  |  | |||
							
								
								
									
										133
									
								
								src/test/finite_set.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/test/finite_set.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,133 @@ | |||
| /*++
 | ||||
| Copyright (c) 2025 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     tst_finite_set.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Test finite sets decl plugin | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     GitHub Copilot Agent 2025 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| #include "ast/ast.h" | ||||
| #include "ast/finite_set_decl_plugin.h" | ||||
| #include "ast/reg_decl_plugins.h" | ||||
| #include "ast/arith_decl_plugin.h" | ||||
| #include "ast/array_decl_plugin.h" | ||||
| 
 | ||||
| static void tst_finite_set_basic() { | ||||
|     ast_manager m; | ||||
|     reg_decl_plugins(m); | ||||
|      | ||||
|     finite_set_util fsets(m); | ||||
|     arith_util arith(m); | ||||
|      | ||||
|     // Test creating a finite set sort
 | ||||
|     sort_ref int_sort(arith.mk_int(), m); | ||||
|     parameter param(int_sort.get()); | ||||
|     sort_ref finite_set_int(m.mk_sort(fsets.get_family_id(), FINITE_SET_SORT, 1, ¶m), m); | ||||
|      | ||||
|     ENSURE(fsets.is_finite_set(finite_set_int.get())); | ||||
|      | ||||
|     // Test creating empty set
 | ||||
|     app_ref empty_set(fsets.mk_empty(int_sort), m); | ||||
|     ENSURE(fsets.is_empty(empty_set.get())); | ||||
|     ENSURE(empty_set->get_sort() == finite_set_int.get()); | ||||
|      | ||||
|     // Test set.singleton
 | ||||
|     expr_ref five(arith.mk_int(5), m); | ||||
|     app_ref singleton_set(fsets.mk_singleton(five), m); | ||||
|     ENSURE(fsets.is_singleton(singleton_set.get())); | ||||
|     ENSURE(singleton_set->get_sort() == finite_set_int.get()); | ||||
|      | ||||
|     // Test set.range
 | ||||
|     expr_ref zero(arith.mk_int(0), m); | ||||
|     expr_ref ten(arith.mk_int(10), m); | ||||
|     app_ref range_set(fsets.mk_range(zero, ten), m); | ||||
|     ENSURE(fsets.is_range(range_set.get())); | ||||
|     ENSURE(range_set->get_sort() == finite_set_int.get()); | ||||
|      | ||||
|     // Test set.union
 | ||||
|     app_ref union_set(fsets.mk_union(empty_set, range_set), m); | ||||
|     ENSURE(fsets.is_union(union_set.get())); | ||||
|     ENSURE(union_set->get_sort() == finite_set_int.get()); | ||||
|      | ||||
|     // Test set.intersect
 | ||||
|     app_ref intersect_set(fsets.mk_intersect(range_set, range_set), m); | ||||
|     ENSURE(fsets.is_intersect(intersect_set.get())); | ||||
|     ENSURE(intersect_set->get_sort() == finite_set_int.get()); | ||||
|      | ||||
|     // Test set.difference
 | ||||
|     app_ref diff_set(fsets.mk_difference(range_set, empty_set), m); | ||||
|     ENSURE(fsets.is_difference(diff_set.get())); | ||||
|     ENSURE(diff_set->get_sort() == finite_set_int.get()); | ||||
|      | ||||
|     // Test set.in
 | ||||
|     app_ref in_expr(fsets.mk_in(five, range_set), m); | ||||
|     ENSURE(fsets.is_in(in_expr.get())); | ||||
|     ENSURE(m.is_bool(in_expr->get_sort())); | ||||
|      | ||||
|     // Test set.size
 | ||||
|     app_ref size_expr(fsets.mk_size(range_set), m); | ||||
|     ENSURE(fsets.is_size(size_expr.get())); | ||||
|     ENSURE(arith.is_int(size_expr->get_sort())); | ||||
|      | ||||
|     // Test set.subset
 | ||||
|     app_ref subset_expr(fsets.mk_subset(empty_set, range_set), m); | ||||
|     ENSURE(fsets.is_subset(subset_expr.get())); | ||||
|     ENSURE(m.is_bool(subset_expr->get_sort())); | ||||
| } | ||||
| 
 | ||||
| static void tst_finite_set_map_select() { | ||||
|     ast_manager m; | ||||
|     reg_decl_plugins(m); | ||||
|      | ||||
|     finite_set_util fsets(m); | ||||
|     arith_util arith(m); | ||||
|     array_util autil(m); | ||||
|      | ||||
|     // Create Int and Bool sorts
 | ||||
|     sort_ref int_sort(arith.mk_int(), m); | ||||
|     sort_ref bool_sort(m.mk_bool_sort(), m); | ||||
|      | ||||
|     // Create finite set sorts
 | ||||
|     parameter int_param(int_sort.get()); | ||||
|     sort_ref finite_set_int(m.mk_sort(fsets.get_family_id(), FINITE_SET_SORT, 1, &int_param), m); | ||||
|      | ||||
|     // Create Array (Int Int) sort for map
 | ||||
|     sort_ref arr_int_int(autil.mk_array_sort(int_sort, int_sort), m); | ||||
|      | ||||
|     // Create a const array (conceptually represents the function)
 | ||||
|     app_ref arr_map(autil.mk_const_array(arr_int_int, arith.mk_int(42)), m); | ||||
|      | ||||
|     // Create a set and test map
 | ||||
|     expr_ref zero(arith.mk_int(0), m); | ||||
|     expr_ref ten(arith.mk_int(10), m); | ||||
|     app_ref range_set(fsets.mk_range(zero, ten), m); | ||||
|      | ||||
|     app_ref mapped_set(fsets.mk_map(arr_map, range_set), m); | ||||
|     ENSURE(fsets.is_map(mapped_set.get())); | ||||
|     ENSURE(fsets.is_finite_set(mapped_set->get_sort())); | ||||
|      | ||||
|     // Create Array (Int Bool) sort for select
 | ||||
|     sort_ref arr_int_bool(autil.mk_array_sort(int_sort, bool_sort), m); | ||||
|      | ||||
|     // Create a const array for select (conceptually represents predicate)
 | ||||
|     app_ref arr_select(autil.mk_const_array(arr_int_bool, m.mk_true()), m); | ||||
|      | ||||
|     app_ref selected_set(fsets.mk_select(arr_select, range_set), m); | ||||
|     ENSURE(fsets.is_select(selected_set.get())); | ||||
|     ENSURE(selected_set->get_sort() == finite_set_int.get()); | ||||
| } | ||||
| 
 | ||||
| void tst_finite_set() { | ||||
|     tst_finite_set_basic(); | ||||
|     tst_finite_set_map_select(); | ||||
| } | ||||
|  | @ -282,4 +282,5 @@ int main(int argc, char ** argv) { | |||
|     TST(scoped_vector); | ||||
|     TST(sls_seq_plugin); | ||||
|     TST(ho_matcher); | ||||
|     TST(finite_set); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue