mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	add finite sets to datatype recursion, delay initialize finite_set plugin, fix bugs in are_distinct and equality simplification
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									d847a28589
								
							
						
					
					
						commit
						4630373a97
					
				
					 9 changed files with 297 additions and 72 deletions
				
			
		|  | @ -26,6 +26,19 @@ Revision History: | ||||||
| 
 | 
 | ||||||
| finite_set_decl_plugin::finite_set_decl_plugin(): | finite_set_decl_plugin::finite_set_decl_plugin(): | ||||||
|     m_init(false) { |     m_init(false) { | ||||||
|  |     m_names.resize(LAST_FINITE_SET_OP, nullptr); | ||||||
|  |     m_names[OP_FINITE_SET_EMPTY] = "set.empty "; | ||||||
|  |     m_names[OP_FINITE_SET_SINGLETON] = "set.singleton"; | ||||||
|  |     m_names[OP_FINITE_SET_UNION] = "set.union"; | ||||||
|  |     m_names[OP_FINITE_SET_INTERSECT] = "set.intersect"; | ||||||
|  |     m_names[OP_FINITE_SET_DIFFERENCE] = "set.difference"; | ||||||
|  |     m_names[OP_FINITE_SET_IN] = "set.in"; | ||||||
|  |     m_names[OP_FINITE_SET_SIZE] = "set.size"; | ||||||
|  |     m_names[OP_FINITE_SET_SUBSET] = "set.subset"; | ||||||
|  |     m_names[OP_FINITE_SET_MAP] = "set.map"; | ||||||
|  |     m_names[OP_FINITE_SET_FILTER] = "set.filter"; | ||||||
|  |     m_names[OP_FINITE_SET_RANGE] = "set.range"; | ||||||
|  |     m_names[OP_FINITE_SET_EXT] = "set.diff"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| finite_set_decl_plugin::~finite_set_decl_plugin() { | finite_set_decl_plugin::~finite_set_decl_plugin() { | ||||||
|  | @ -59,18 +72,18 @@ void finite_set_decl_plugin::init() { | ||||||
|     sort* intintT[2] = { intT, intT }; |     sort* intintT[2] = { intT, intT }; | ||||||
|      |      | ||||||
|     m_sigs.resize(LAST_FINITE_SET_OP); |     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_EMPTY]      = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_SINGLETON]  = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_UNION]      = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_INTERSECT]  = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_DIFFERENCE] = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_IN]         = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_SIZE]       = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_SUBSET]     = alloc(polymorphism::psig, m, m_names[OP_FINITE_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_MAP]        = alloc(polymorphism::psig, m, m_names[OP_FINITE_SET_MAP], 2, 2, arrABsetA, setB); | ||||||
|     m_sigs[OP_FINITE_SET_FILTER]     = alloc(polymorphism::psig, m, "set.filter",     1, 2, arrABoolsetA, setA); |     m_sigs[OP_FINITE_SET_FILTER]     = alloc(polymorphism::psig, m, m_names[OP_FINITE_SET_FILTER], 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_RANGE]      = alloc(polymorphism::psig, m, m_names[OP_FINITE_SET_RANGE],      0, 2, intintT, setInt); | ||||||
|     m_sigs[OP_FINITE_SET_EXT]       = alloc(polymorphism::psig, m, "set.diff", 1, 2, setAsetA, A); |     m_sigs[OP_FINITE_SET_EXT]        = alloc(polymorphism::psig, m, m_names[OP_FINITE_SET_EXT], 1, 2, setAsetA, A); | ||||||
| //    m_sigs[OP_FINITE_SET_MAP_INVERSE] = alloc(polymorphism::psig, m, "set.map_inverse", 2, 3, arrABsetBsetA, A);
 | //    m_sigs[OP_FINITE_SET_MAP_INVERSE] = alloc(polymorphism::psig, m, "set.map_inverse", 2, 3, arrABsetBsetA, A);
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -187,11 +200,9 @@ func_decl * finite_set_decl_plugin::mk_func_decl(decl_kind k, unsigned num_param | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void finite_set_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol const & logic) { | void finite_set_decl_plugin::get_op_names(svector<builtin_name>& op_names, symbol const & logic) { | ||||||
|     init(); |     for (unsigned i = 0; i < m_names.size(); ++i)  | ||||||
|     for (unsigned i = 0; i < m_sigs.size(); ++i) { |         if (m_names[i]) | ||||||
|         if (m_sigs[i]) |             op_names.push_back(builtin_name(std::string(m_names[i]), 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) { | void finite_set_decl_plugin::get_sort_names(svector<builtin_name>& sort_names, symbol const & logic) { | ||||||
|  | @ -282,8 +293,10 @@ bool finite_set_decl_plugin::is_value(app * e) const { | ||||||
| 
 | 
 | ||||||
| bool finite_set_decl_plugin::is_unique_value(app* e) const { | bool finite_set_decl_plugin::is_unique_value(app* e) const { | ||||||
|     // Empty set is a value
 |     // Empty set is a value
 | ||||||
|  |     // A singleton of a unique value is tagged as unique
 | ||||||
|  |     // ranges are not considered unique even if the bounds are values
 | ||||||
|     return is_app_of(e, m_family_id, OP_FINITE_SET_EMPTY) || |     return is_app_of(e, m_family_id, OP_FINITE_SET_EMPTY) || | ||||||
|            (is_app_of(e, m_family_id, OP_FINITE_SET_SINGLETON) && is_unique_value(to_app(e->get_arg(0)))); |            (is_app_of(e, m_family_id, OP_FINITE_SET_SINGLETON) && m_manager->is_unique_value(to_app(e->get_arg(0)))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool finite_set_decl_plugin::are_distinct(app* e1, app* e2) const { | bool finite_set_decl_plugin::are_distinct(app* e1, app* e2) const { | ||||||
|  | @ -294,8 +307,9 @@ bool finite_set_decl_plugin::are_distinct(app* e1, app* e2) const { | ||||||
|         return true; |         return true; | ||||||
|     if (r.is_singleton(e1) && r.is_empty(e2)) |     if (r.is_singleton(e1) && r.is_empty(e2)) | ||||||
|         return true; |         return true; | ||||||
|     if(r.is_singleton(e1) && r.is_singleton(e2)) |     expr *x = nullptr, *y = nullptr; | ||||||
|         return m_manager->are_distinct(e1, e2); |     if(r.is_singleton(e1, x) && r.is_singleton(e2, y)) | ||||||
|  |         return m_manager->are_distinct(x, y); | ||||||
|      |      | ||||||
|     // TODO: could be extended to cases where we can prove the sets are different by containing one element
 |     // TODO: could be extended to cases where we can prove the sets are different by containing one element
 | ||||||
|     // that the other doesn't contain. Such as (union (singleton a) (singleton b)) and (singleton c) where c is different from a, b.
 |     // that the other doesn't contain. Such as (union (singleton a) (singleton b)) and (singleton c) where c is different from a, b.
 | ||||||
|  |  | ||||||
|  | @ -54,7 +54,8 @@ enum finite_set_op_kind { | ||||||
| 
 | 
 | ||||||
| class finite_set_decl_plugin : public decl_plugin { | class finite_set_decl_plugin : public decl_plugin { | ||||||
|     ptr_vector<polymorphism::psig>   m_sigs; |     ptr_vector<polymorphism::psig>   m_sigs; | ||||||
|     bool                             m_init; |     svector<char const*>             m_names; | ||||||
|  |     bool                             m_init = false; | ||||||
| 
 | 
 | ||||||
|     void init(); |     void init(); | ||||||
|     func_decl * mk_empty(sort* set_sort); |     func_decl * mk_empty(sort* set_sort); | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ Author: | ||||||
| 
 | 
 | ||||||
| #include "ast/rewriter/finite_set_rewriter.h" | #include "ast/rewriter/finite_set_rewriter.h" | ||||||
| #include "ast/arith_decl_plugin.h" | #include "ast/arith_decl_plugin.h" | ||||||
|  | #include "ast/ast_pp.h" | ||||||
| 
 | 
 | ||||||
| br_status finite_set_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { | br_status finite_set_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { | ||||||
|     SASSERT(f->get_family_id() == get_fid()); |     SASSERT(f->get_family_id() == get_fid()); | ||||||
|  | @ -39,7 +40,6 @@ br_status finite_set_rewriter::mk_app_core(func_decl * f, unsigned num_args, exp | ||||||
|         SASSERT(num_args == 2); |         SASSERT(num_args == 2); | ||||||
|         return mk_in(args[0], args[1], result); |         return mk_in(args[0], args[1], result); | ||||||
|     case OP_FINITE_SET_SIZE: |     case OP_FINITE_SET_SIZE: | ||||||
|         // Size is already in normal form, no simplifications
 |  | ||||||
|         return mk_size(args[0], result); |         return mk_size(args[0], result); | ||||||
|     default: |     default: | ||||||
|         return BR_FAILED; |         return BR_FAILED; | ||||||
|  | @ -55,18 +55,18 @@ br_status finite_set_rewriter::mk_union(unsigned num_args, expr * const * args, | ||||||
|      |      | ||||||
|     // Identity: set.union(x, empty) -> x or set.union(empty, x) -> x
 |     // Identity: set.union(x, empty) -> x or set.union(empty, x) -> x
 | ||||||
|     if (num_args == 2) { |     if (num_args == 2) { | ||||||
|         if (m_util.is_empty(args[0])) { |         if (u.is_empty(args[0])) { | ||||||
|             result = args[1]; |             result = args[1]; | ||||||
|             return BR_DONE; |             return BR_DONE; | ||||||
|         } |         } | ||||||
|         if (m_util.is_empty(args[1])) { |         if (u.is_empty(args[1])) { | ||||||
|             result = args[0]; |             result = args[0]; | ||||||
|             return BR_DONE; |             return BR_DONE; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // Absorption: set.union(x, set.intersect(x, y)) -> x
 |         // Absorption: set.union(x, set.intersect(x, y)) -> x
 | ||||||
|         expr* a1, *a2; |         expr* a1, *a2; | ||||||
|         if (m_util.is_intersect(args[1], a1, a2)) { |         if (u.is_intersect(args[1], a1, a2)) { | ||||||
|             if (args[0] == a1 || args[0] == a2) { |             if (args[0] == a1 || args[0] == a2) { | ||||||
|                 result = args[0]; |                 result = args[0]; | ||||||
|                 return BR_DONE; |                 return BR_DONE; | ||||||
|  | @ -74,7 +74,7 @@ br_status finite_set_rewriter::mk_union(unsigned num_args, expr * const * args, | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // Absorption: set.union(set.intersect(x, y), x) -> x
 |         // Absorption: set.union(set.intersect(x, y), x) -> x
 | ||||||
|         if (m_util.is_intersect(args[0], a1, a2)) { |         if (u.is_intersect(args[0], a1, a2)) { | ||||||
|             if (args[1] == a1 || args[1] == a2) { |             if (args[1] == a1 || args[1] == a2) { | ||||||
|                 result = args[1]; |                 result = args[1]; | ||||||
|                 return BR_DONE; |                 return BR_DONE; | ||||||
|  | @ -94,18 +94,18 @@ br_status finite_set_rewriter::mk_intersect(unsigned num_args, expr * const * ar | ||||||
|      |      | ||||||
|     // Annihilation: set.intersect(x, empty) -> empty or set.intersect(empty, x) -> empty
 |     // Annihilation: set.intersect(x, empty) -> empty or set.intersect(empty, x) -> empty
 | ||||||
|     if (num_args == 2) { |     if (num_args == 2) { | ||||||
|         if (m_util.is_empty(args[0])) { |         if (u.is_empty(args[0])) { | ||||||
|             result = args[0]; |             result = args[0]; | ||||||
|             return BR_DONE; |             return BR_DONE; | ||||||
|         } |         } | ||||||
|         if (m_util.is_empty(args[1])) { |         if (u.is_empty(args[1])) { | ||||||
|             result = args[1]; |             result = args[1]; | ||||||
|             return BR_DONE; |             return BR_DONE; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // Absorption: set.intersect(x, set.union(x, y)) -> x
 |         // Absorption: set.intersect(x, set.union(x, y)) -> x
 | ||||||
|         expr* a1, *a2; |         expr* a1, *a2; | ||||||
|         if (m_util.is_union(args[1], a1, a2)) { |         if (u.is_union(args[1], a1, a2)) { | ||||||
|             if (args[0] == a1 || args[0] == a2) { |             if (args[0] == a1 || args[0] == a2) { | ||||||
|                 result = args[0]; |                 result = args[0]; | ||||||
|                 return BR_DONE; |                 return BR_DONE; | ||||||
|  | @ -113,7 +113,7 @@ br_status finite_set_rewriter::mk_intersect(unsigned num_args, expr * const * ar | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // Absorption: set.intersect(set.union(x, y), x) -> x
 |         // Absorption: set.intersect(set.union(x, y), x) -> x
 | ||||||
|         if (m_util.is_union(args[0], a1, a2)) { |         if (u.is_union(args[0], a1, a2)) { | ||||||
|             if (args[1] == a1 || args[1] == a2) { |             if (args[1] == a1 || args[1] == a2) { | ||||||
|                 result = args[1]; |                 result = args[1]; | ||||||
|                 return BR_DONE; |                 return BR_DONE; | ||||||
|  | @ -128,19 +128,19 @@ br_status finite_set_rewriter::mk_difference(expr * arg1, expr * arg2, expr_ref | ||||||
|     // set.difference(x, x) -> set.empty
 |     // set.difference(x, x) -> set.empty
 | ||||||
|     if (arg1 == arg2) { |     if (arg1 == arg2) { | ||||||
|         sort* set_sort = arg1->get_sort(); |         sort* set_sort = arg1->get_sort(); | ||||||
|         SASSERT(m_util.is_finite_set(set_sort)); |         SASSERT(u.is_finite_set(set_sort)); | ||||||
|         result = m_util.mk_empty(set_sort); |         result = u.mk_empty(set_sort); | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Identity: set.difference(x, empty) -> x
 |     // Identity: set.difference(x, empty) -> x
 | ||||||
|     if (m_util.is_empty(arg2)) { |     if (u.is_empty(arg2)) { | ||||||
|         result = arg1; |         result = arg1; | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // Annihilation: set.difference(empty, x) -> empty
 |     // Annihilation: set.difference(empty, x) -> empty
 | ||||||
|     if (m_util.is_empty(arg1)) { |     if (u.is_empty(arg1)) { | ||||||
|         result = arg1; |         result = arg1; | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|  | @ -156,20 +156,20 @@ br_status finite_set_rewriter::mk_subset(expr * arg1, expr * arg2, expr_ref & re | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // set.subset(empty, x) -> true
 |     // set.subset(empty, x) -> true
 | ||||||
|     if (m_util.is_empty(arg1)) { |     if (u.is_empty(arg1)) { | ||||||
|         result = m.mk_true(); |         result = m.mk_true(); | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // set.subset(x, empty) -> x = empty
 |     // set.subset(x, empty) -> x = empty
 | ||||||
|     if (m_util.is_empty(arg2)) { |     if (u.is_empty(arg2)) { | ||||||
|         result = m.mk_eq(arg1, arg2); |         result = m.mk_eq(arg1, arg2); | ||||||
|         return BR_REWRITE1; |         return BR_REWRITE1; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // General case: set.subset(x, y) -> set.intersect(x, y) = x
 |     // General case: set.subset(x, y) -> set.intersect(x, y) = x
 | ||||||
|     expr_ref intersect(m); |     expr_ref intersect(m); | ||||||
|     intersect = m_util.mk_intersect(arg1, arg2); |     intersect = u.mk_intersect(arg1, arg2); | ||||||
|     result = m.mk_eq(intersect, arg1); |     result = m.mk_eq(intersect, arg1); | ||||||
|     return BR_REWRITE3; |     return BR_REWRITE3; | ||||||
| } | } | ||||||
|  | @ -181,18 +181,18 @@ br_status finite_set_rewriter::mk_singleton(expr * arg, expr_ref & result) { | ||||||
| 
 | 
 | ||||||
| br_status finite_set_rewriter::mk_size(expr * arg, expr_ref & result) { | br_status finite_set_rewriter::mk_size(expr * arg, expr_ref & result) { | ||||||
|     arith_util a(m); |     arith_util a(m); | ||||||
|     if (m_util.is_empty(arg)) { |     if (u.is_empty(arg)) { | ||||||
|         // size(empty) -> 0
 |         // size(empty) -> 0
 | ||||||
|         result = a.mk_int(0); |         result = a.mk_int(0); | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|     if (m_util.is_singleton(arg)) { |     if (u.is_singleton(arg)) { | ||||||
|         // size(singleton(x)) -> 1
 |         // size(singleton(x)) -> 1
 | ||||||
|         result = a.mk_int(1); |         result = a.mk_int(1); | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|     expr *lower, *upper; |     expr *lower, *upper; | ||||||
|     if (m_util.is_range(arg, lower, upper)) { |     if (u.is_range(arg, lower, upper)) { | ||||||
|         // size(range(a, b)) -> b - a + 1
 |         // size(range(a, b)) -> b - a + 1
 | ||||||
|         expr_ref size_expr(m); |         expr_ref size_expr(m); | ||||||
|         size_expr = a.mk_add(a.mk_sub(upper, lower), a.mk_int(1)); |         size_expr = a.mk_add(a.mk_sub(upper, lower), a.mk_int(1)); | ||||||
|  | @ -205,14 +205,14 @@ br_status finite_set_rewriter::mk_size(expr * arg, expr_ref & result) { | ||||||
| 
 | 
 | ||||||
| br_status finite_set_rewriter::mk_in(expr * elem, expr * set, expr_ref & result) { | br_status finite_set_rewriter::mk_in(expr * elem, expr * set, expr_ref & result) { | ||||||
|     // set.in(x, empty) -> false
 |     // set.in(x, empty) -> false
 | ||||||
|     if (m_util.is_empty(set)) { |     if (u.is_empty(set)) { | ||||||
|         result = m.mk_false(); |         result = m.mk_false(); | ||||||
|         return BR_DONE; |         return BR_DONE; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // set.in(x, singleton(y)) checks
 |     // set.in(x, singleton(y)) checks
 | ||||||
|     expr* singleton_elem; |     expr* singleton_elem; | ||||||
|     if (m_util.is_singleton(set, singleton_elem)) { |     if (u.is_singleton(set, singleton_elem)) { | ||||||
|         // set.in(x, singleton(x)) -> true (when x is the same)
 |         // set.in(x, singleton(x)) -> true (when x is the same)
 | ||||||
|         if (elem == singleton_elem) { |         if (elem == singleton_elem) { | ||||||
|             result = m.mk_true(); |             result = m.mk_true(); | ||||||
|  | @ -222,6 +222,12 @@ br_status finite_set_rewriter::mk_in(expr * elem, expr * set, expr_ref & result) | ||||||
|         result = m.mk_eq(elem, singleton_elem); |         result = m.mk_eq(elem, singleton_elem); | ||||||
|         return BR_REWRITE1; |         return BR_REWRITE1; | ||||||
|     } |     } | ||||||
|  |     expr *lo = nullptr, *hi = nullptr; | ||||||
|  |     if (u.is_range(set, lo, hi)) { | ||||||
|  |         arith_util a(m); | ||||||
|  |         result = m.mk_and(a.mk_le(lo, elem), a.mk_le(elem, hi)); | ||||||
|  |         return BR_REWRITE2; | ||||||
|  |     } | ||||||
|     // NB we don't rewrite (set.in x (set.union s t)) to (or (set.in x s) (set.in x t))
 |     // NB we don't rewrite (set.in x (set.union s t)) to (or (set.in x s) (set.in x t))
 | ||||||
|     // because it creates two new sub-expressions. The expression (set.union s t) could
 |     // because it creates two new sub-expressions. The expression (set.union s t) could
 | ||||||
|     // be shared with other expressions so the net effect of this rewrite could be to create
 |     // be shared with other expressions so the net effect of this rewrite could be to create
 | ||||||
|  | @ -248,18 +254,169 @@ br_status finite_set_rewriter::mk_in(expr * elem, expr * set, expr_ref & result) | ||||||
| * min({}) = {} | * min({}) = {} | ||||||
| * min([l..u]) = [l..u] u {} | * min([l..u]) = [l..u] u {} | ||||||
| * min(s u t) =  | * min(s u t) =  | ||||||
| *   let range_s u s1 = min(s) | *   let {x} u s1 = min(s) | ||||||
| *   let range_t u t1 = min(t) | *   let {y} u t1 = min(t) | ||||||
| *   if range_s < range_t:  | *   if x = y then | ||||||
| *        range_s u (t u s1) | *        { x } u (s1 u t1) | ||||||
| *   if range_t < range_t: | *   else  if x < y then  | ||||||
| *        range_t u (s u t1) | *        {x} u (s1 u ({y} u t1) | ||||||
| *   if range_t n range_s != {}: | *   else // x > y 
 | ||||||
| *        min(range_t, range_s) u the rest ... | *        {y} u (t1 u ({x} u s1) | ||||||
| *   etc. | *    | ||||||
|  | *  Handling ranges is TBD | ||||||
|  | *  For proper range handling we have to change is_less on numeric singleton sets | ||||||
|  | *  to use the numerical value, not the expression identifier. Then the ordering | ||||||
|  | *  has to make all numeric values less than symbolic values. | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| br_status finite_set_rewriter::mk_eq_core(expr* a, expr* b, expr_ref& result) { | bool finite_set_rewriter::is_less(expr *a, expr *b) { | ||||||
|  |     return a->get_id() < b->get_id(); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | expr* finite_set_rewriter::mk_union(expr* a, expr* b) { | ||||||
|  |     if (u.is_empty(a)) | ||||||
|  |         return b; | ||||||
|  |     if (u.is_empty(b)) | ||||||
|  |         return a; | ||||||
|  |     if (a == b) | ||||||
|  |         return a; | ||||||
|  |     return u.mk_union(a, b); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr* finite_set_rewriter::min(expr* e) { | ||||||
|  |     if (m_is_min.is_marked(e)) | ||||||
|  |         return e; | ||||||
|  |     expr *a = nullptr, *b = nullptr; | ||||||
|  |     if (u.is_union(e, a, b)) { | ||||||
|  |         a = min(a); | ||||||
|  |         b = min(b); | ||||||
|  |         if (u.is_empty(a)) | ||||||
|  |             return b; | ||||||
|  |         if (u.is_empty(b)) | ||||||
|  |             return a; | ||||||
|  |         auto [x,a1] = get_min(a); | ||||||
|  |         auto [y,b1] = get_min(b); | ||||||
|  |         if (x == y) | ||||||
|  |             a = mk_union(x, mk_union(a1, b1)); | ||||||
|  |         else if (is_less(x, y)) | ||||||
|  |             a = mk_union(x, mk_union(a1, b)); | ||||||
|  |         else | ||||||
|  |             a = mk_union(y, mk_union(a, b1)); | ||||||
|  |         m_pinned.push_back(a); | ||||||
|  |         m_is_min.mark(a); | ||||||
|  |         return a; | ||||||
|  |     } | ||||||
|  |     if (u.is_intersect(e, a, b)) { | ||||||
|  |         if (!from_unique_values(a) || !from_unique_values(b)) { | ||||||
|  |             m_pinned.push_back(e); | ||||||
|  |             m_is_min.mark(e); | ||||||
|  |             return e; | ||||||
|  |         } | ||||||
|  |         while (true) { | ||||||
|  |             a = min(a); | ||||||
|  |             b = min(b); | ||||||
|  |             if (u.is_empty(a)) | ||||||
|  |                 return a; | ||||||
|  |             if (u.is_empty(b)) | ||||||
|  |                 return b; | ||||||
|  |             auto [x, a1] = get_min(a); | ||||||
|  |             auto [y, b1] = get_min(b); | ||||||
|  |             if (x == y) { | ||||||
|  |                 a = mk_union(x, u.mk_intersect(a1, b1)); | ||||||
|  |                 m_pinned.push_back(a); | ||||||
|  |                 m_is_min.mark(a); | ||||||
|  |                 return a; | ||||||
|  |             } | ||||||
|  |             else if (is_less(x, y)) | ||||||
|  |                 a = a1; | ||||||
|  |             else | ||||||
|  |                 b = b1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (u.is_difference(e, a, b)) { | ||||||
|  |         if (!from_unique_values(a) || !from_unique_values(b)) { | ||||||
|  |             m_pinned.push_back(e); | ||||||
|  |             m_is_min.mark(e); | ||||||
|  |             return e; | ||||||
|  |         } | ||||||
|  |         while (true) { | ||||||
|  |             a = min(a); | ||||||
|  |             b = min(b); | ||||||
|  |             if (u.is_empty(a) || u.is_empty(b)) | ||||||
|  |                 return a; | ||||||
|  |             auto [x, a1] = get_min(a); | ||||||
|  |             auto [y, b1] = get_min(b); | ||||||
|  |             if (x == y) { | ||||||
|  |                 a = a1; | ||||||
|  |                 b = b1; | ||||||
|  |             } | ||||||
|  |             else if (is_less(x, y)) { | ||||||
|  |                 a = mk_union(x, u.mk_difference(a1, b)); | ||||||
|  |                 m_pinned.push_back(a); | ||||||
|  |                 m_is_min.mark(a); | ||||||
|  |                 return a; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 b = b1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // set.filter, set.map don't have decompositions
 | ||||||
|  |     m_pinned.push_back(e); | ||||||
|  |     m_is_min.mark(e); | ||||||
|  |     return e; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::pair<expr*, expr*> finite_set_rewriter::get_min(expr* a) { | ||||||
|  |     expr *x = nullptr, *y = nullptr; | ||||||
|  |     if (u.is_union(a, x, y)) | ||||||
|  |         return {x, y}; | ||||||
|  |     auto empty = u.mk_empty(a->get_sort()); | ||||||
|  |     m_pinned.push_back(empty); | ||||||
|  |     return {a, empty}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | br_status finite_set_rewriter::mk_eq_core(expr *a, expr *b, expr_ref &result) { | ||||||
|  |     m_is_min.reset(); | ||||||
|  |     m_pinned.reset(); | ||||||
|  |     bool are_unique = true; | ||||||
|  |     while (true) { | ||||||
|  |         if (a == b) { | ||||||
|  |             result = m.mk_true(); | ||||||
|  |             return BR_DONE; | ||||||
|  |         } | ||||||
|  |         TRACE(finite_set, tout << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n"); | ||||||
|  |         a = min(a); | ||||||
|  |         b = min(b); | ||||||
|  |         auto [x, a1] = get_min(a); | ||||||
|  |         auto [y, b1] = get_min(b); | ||||||
|  | 
 | ||||||
|  |         // only empty sets and singletons of unique values are unique.
 | ||||||
|  |         // ranges are not counted as unique.
 | ||||||
|  |         are_unique &= m.is_unique_value(x) && m.is_unique_value(y); | ||||||
|  |         a = a1; | ||||||
|  |         b = b1; | ||||||
|  |         if (x == y) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         if (m.are_distinct(x, y) && are_unique) { | ||||||
|  |             are_unique &= from_unique_values(a); | ||||||
|  |             are_unique &= from_unique_values(b); | ||||||
|  |             if (are_unique) { | ||||||
|  |                 result = m.mk_false(); | ||||||
|  |                 return BR_DONE; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|         return BR_FAILED; |         return BR_FAILED; | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool finite_set_rewriter::from_unique_values(expr *a) { | ||||||
|  |     while (!u.is_empty(a)) { | ||||||
|  |         auto [x, a1] = get_min(min(a)); | ||||||
|  |         if (!m.is_unique_value(x)) | ||||||
|  |             return false; | ||||||
|  |         a = a1; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -35,7 +35,15 @@ where the signature is defined in finite_set_decl_plugin.h. | ||||||
| class finite_set_rewriter { | class finite_set_rewriter { | ||||||
|     friend class finite_set_rewriter_test; |     friend class finite_set_rewriter_test; | ||||||
|     ast_manager &m; |     ast_manager &m; | ||||||
|     finite_set_util  m_util; |     finite_set_util  u; | ||||||
|  |     expr_ref_vector m_pinned; | ||||||
|  |     expr_mark m_is_min; | ||||||
|  |      | ||||||
|  |     expr * min(expr *a); | ||||||
|  |     std::pair<expr *, expr *> get_min(expr *a); | ||||||
|  |     bool is_less(expr *a, expr *b); | ||||||
|  |     expr *mk_union(expr *a, expr *b); | ||||||
|  |     bool from_unique_values(expr *a); | ||||||
| 
 | 
 | ||||||
|     // Rewrite rules for set operations
 |     // Rewrite rules for set operations
 | ||||||
|     br_status mk_union(unsigned num_args, expr *const *args, expr_ref &result); |     br_status mk_union(unsigned num_args, expr *const *args, expr_ref &result); | ||||||
|  | @ -48,11 +56,11 @@ class finite_set_rewriter { | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     finite_set_rewriter(ast_manager & m, params_ref const & p = params_ref()): |     finite_set_rewriter(ast_manager & m, params_ref const & p = params_ref()): | ||||||
|         m(m), m_util(m) { |         m(m), u(m), m_pinned(m) { | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     family_id get_fid() const { return m_util.get_family_id(); } |     family_id get_fid() const { return u.get_family_id(); } | ||||||
|     finite_set_util& util() { return m_util; } |     finite_set_util& util() { return u; } | ||||||
| 
 | 
 | ||||||
|     br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);    |     br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);    | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -335,6 +335,7 @@ namespace smt { | ||||||
|             for (unsigned i = 0; i < num_args; i++) { |             for (unsigned i = 0; i < num_args; i++) { | ||||||
|                 enode * arg = e->get_arg(i); |                 enode * arg = e->get_arg(i); | ||||||
|                 sort * s    = arg->get_sort(); |                 sort * s    = arg->get_sort(); | ||||||
|  |                 sort *e_sort = nullptr; | ||||||
|                 if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { |                 if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { | ||||||
|                     app_ref def(m_autil.mk_default(arg->get_expr()), m); |                     app_ref def(m_autil.mk_default(arg->get_expr()), m); | ||||||
|                     if (!ctx.e_internalized(def)) { |                     if (!ctx.e_internalized(def)) { | ||||||
|  | @ -342,6 +343,13 @@ namespace smt { | ||||||
|                     } |                     } | ||||||
|                     arg = ctx.get_enode(def);        |                     arg = ctx.get_enode(def);        | ||||||
|                 } |                 } | ||||||
|  |                 if (m_fsutil.is_finite_set(s, e_sort) && m_util.is_datatype(e_sort)) { | ||||||
|  |                     app_ref def(m_fsutil.mk_empty(s), m); | ||||||
|  |                     if (!ctx.e_internalized(def)) { | ||||||
|  |                         ctx.internalize(def, false); | ||||||
|  |                     } | ||||||
|  |                     arg = ctx.get_enode(def);       | ||||||
|  |                 } | ||||||
|                 if (!m_util.is_datatype(s) && !m_sutil.is_seq(s)) |                 if (!m_util.is_datatype(s) && !m_sutil.is_seq(s)) | ||||||
|                     continue; |                     continue; | ||||||
|                 if (is_attached_to_var(arg)) |                 if (is_attached_to_var(arg)) | ||||||
|  | @ -532,8 +540,9 @@ namespace smt { | ||||||
|                 found = true; |                 found = true; | ||||||
|             } |             } | ||||||
|             sort * s = arg->get_sort(); |             sort * s = arg->get_sort(); | ||||||
|             if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { |             sort *se = nullptr; | ||||||
|                 for (enode* aarg : get_array_args(arg)) { |             auto add_args = [&](ptr_vector<enode> const &args) { | ||||||
|  |                 for (enode *aarg : args) { | ||||||
|                     if (aarg->get_root() == child->get_root()) { |                     if (aarg->get_root() == child->get_root()) { | ||||||
|                         if (aarg != child) { |                         if (aarg != child) { | ||||||
|                             m_used_eqs.push_back(enode_pair(aarg, child)); |                             m_used_eqs.push_back(enode_pair(aarg, child)); | ||||||
|  | @ -541,17 +550,16 @@ namespace smt { | ||||||
|                         found = true; |                         found = true; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |             }; | ||||||
|  |             if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { | ||||||
|  |                 add_args(get_array_args(arg)); | ||||||
|  |             } | ||||||
|  |             if (m_fsutil.is_finite_set(s, se) && m_util.is_datatype(se)) { | ||||||
|  |                 add_args(get_finite_set_args(arg)); | ||||||
|             } |             } | ||||||
|             sort* se = nullptr; |  | ||||||
|             if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) { |             if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) { | ||||||
|                 enode* sibling; |                 enode *sibling = nullptr; | ||||||
|                 for (enode* aarg : get_seq_args(arg, sibling)) { |                 add_args(get_seq_args(arg, sibling)); | ||||||
|                     if (aarg->get_root() == child->get_root()) { |  | ||||||
|                         if (aarg != child)  |  | ||||||
|                             m_used_eqs.push_back(enode_pair(aarg, child)); |  | ||||||
|                         found = true; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 if (sibling && sibling != arg) |                 if (sibling && sibling != arg) | ||||||
|                     m_used_eqs.push_back(enode_pair(arg, sibling)); |                     m_used_eqs.push_back(enode_pair(arg, sibling)); | ||||||
| 
 | 
 | ||||||
|  | @ -640,6 +648,11 @@ namespace smt { | ||||||
|                         return true; |                         return true; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |             else if (m_fsutil.is_finite_set(s, se) && m_util.is_datatype(se)) { | ||||||
|  |                 for (enode *aarg : get_finite_set_args(arg)) | ||||||
|  |                     if (process_arg(aarg)) | ||||||
|  |                         return true; | ||||||
|  |             } | ||||||
|             else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { |             else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) { | ||||||
|                 for (enode* aarg : get_array_args(arg))  |                 for (enode* aarg : get_array_args(arg))  | ||||||
|                     if (process_arg(aarg)) |                     if (process_arg(aarg)) | ||||||
|  | @ -649,6 +662,33 @@ namespace smt { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     ptr_vector<enode> const &theory_datatype::get_finite_set_args(enode *n) { | ||||||
|  |         m_args.reset(); | ||||||
|  |         m_todo.reset(); | ||||||
|  |         auto add_todo = [&](enode *n) { | ||||||
|  |             if (!n->is_marked()) { | ||||||
|  |                 n->set_mark(); | ||||||
|  |                 m_todo.push_back(n); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         add_todo(n); | ||||||
|  | 
 | ||||||
|  |         for (unsigned i = 0; i < m_todo.size(); ++i) { | ||||||
|  |             enode *n = m_todo[i]; | ||||||
|  |             expr *e = n->get_expr(); | ||||||
|  |             if (m_fsutil.is_singleton(e)) | ||||||
|  |                 m_args.push_back(n->get_arg(0)); | ||||||
|  |             else if (m_fsutil.is_union(e)) | ||||||
|  |                 for (auto k : enode::args(n)) | ||||||
|  |                     add_todo(k); | ||||||
|  |         } | ||||||
|  |         for (enode *n : m_todo) | ||||||
|  |             n->unset_mark(); | ||||||
|  | 
 | ||||||
|  |         return m_args; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n, enode*& sibling) { |     ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n, enode*& sibling) { | ||||||
|         m_args.reset(); |         m_args.reset(); | ||||||
|         m_todo.reset(); |         m_todo.reset(); | ||||||
|  | @ -762,6 +802,7 @@ namespace smt { | ||||||
|         m_util(m), |         m_util(m), | ||||||
|         m_autil(m), |         m_autil(m), | ||||||
|         m_sutil(m), |         m_sutil(m), | ||||||
|  |         m_fsutil(m), | ||||||
|         m_find(*this) { |         m_find(*this) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ Revision History: | ||||||
| #include "util/union_find.h" | #include "util/union_find.h" | ||||||
| #include "ast/array_decl_plugin.h" | #include "ast/array_decl_plugin.h" | ||||||
| #include "ast/seq_decl_plugin.h" | #include "ast/seq_decl_plugin.h" | ||||||
|  | #include "ast/finite_set_decl_plugin.h" | ||||||
| #include "ast/datatype_decl_plugin.h" | #include "ast/datatype_decl_plugin.h" | ||||||
| #include "model/datatype_factory.h" | #include "model/datatype_factory.h" | ||||||
| #include "smt/smt_theory.h" | #include "smt/smt_theory.h" | ||||||
|  | @ -48,6 +49,7 @@ namespace smt { | ||||||
|         datatype_util             m_util; |         datatype_util             m_util; | ||||||
|         array_util                m_autil; |         array_util                m_autil; | ||||||
|         seq_util                  m_sutil; |         seq_util                  m_sutil; | ||||||
|  |         finite_set_util           m_fsutil; | ||||||
|         ptr_vector<var_data>      m_var_data; |         ptr_vector<var_data>      m_var_data; | ||||||
|         th_union_find             m_find; |         th_union_find             m_find; | ||||||
|         trail_stack               m_trail_stack; |         trail_stack               m_trail_stack; | ||||||
|  | @ -95,6 +97,7 @@ namespace smt { | ||||||
|         ptr_vector<enode> m_args, m_todo; |         ptr_vector<enode> m_args, m_todo; | ||||||
|         ptr_vector<enode> const& get_array_args(enode* n); |         ptr_vector<enode> const& get_array_args(enode* n); | ||||||
|         ptr_vector<enode> const& get_seq_args(enode* n, enode*& sibling); |         ptr_vector<enode> const& get_seq_args(enode* n, enode*& sibling); | ||||||
|  |         ptr_vector<enode> const& get_finite_set_args(enode *n); | ||||||
| 
 | 
 | ||||||
|         // class for managing state of final_check
 |         // class for managing state of final_check
 | ||||||
|         class final_check_st { |         class final_check_st { | ||||||
|  |  | ||||||
|  | @ -690,6 +690,7 @@ namespace smt { | ||||||
|                 continue; |                 continue; | ||||||
|             out << "watch[" << i << "] := " << m_clauses.watch[i] << "\n"; |             out << "watch[" << i << "] := " << m_clauses.watch[i] << "\n"; | ||||||
|         } |         } | ||||||
|  |         m_cardinality_solver.display(out); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void theory_finite_set::init_model(model_generator & mg) { |     void theory_finite_set::init_model(model_generator & mg) { | ||||||
|  |  | ||||||
|  | @ -65,7 +65,7 @@ namespace smt { | ||||||
|         expr_ref_vector bs(m); |         expr_ref_vector bs(m); | ||||||
|         for (auto n : ns) { |         for (auto n : ns) { | ||||||
|             std::ostringstream strm; |             std::ostringstream strm; | ||||||
|             strm << enode_pp(n, ctx); |             strm << "|" << enode_pp(n, ctx) << "|"; | ||||||
|             symbol name = symbol(strm.str()); |             symbol name = symbol(strm.str()); | ||||||
|             expr_ref b(m.mk_const(name, m.mk_bool_sort()), m); |             expr_ref b(m.mk_const(name, m.mk_bool_sort()), m); | ||||||
|             bs.push_back(b); |             bs.push_back(b); | ||||||
|  | @ -401,7 +401,7 @@ namespace smt { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::ostream& theory_finite_set_size::display(std::ostream& out) { |     std::ostream& theory_finite_set_size::display(std::ostream& out) const { | ||||||
|         if (m_solver) |         if (m_solver) | ||||||
|             m_solver->display(out); |             m_solver->display(out); | ||||||
|         return out; |         return out; | ||||||
|  |  | ||||||
|  | @ -68,6 +68,6 @@ namespace smt { | ||||||
|         void add_theory_assumptions(expr_ref_vector &assumptions);  |         void add_theory_assumptions(expr_ref_vector &assumptions);  | ||||||
|         bool should_research(expr_ref_vector &unsat_core); |         bool should_research(expr_ref_vector &unsat_core); | ||||||
|         lbool final_check();  |         lbool final_check();  | ||||||
|         std::ostream &display(std::ostream &out); |         std::ostream &display(std::ostream &out) const; | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue