mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 19:52:29 +00:00 
			
		
		
		
	add interpretations when there are ranges
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									65f38eac16
								
							
						
					
					
						commit
						2e4402c8f3
					
				
					 8 changed files with 427 additions and 158 deletions
				
			
		|  | @ -233,14 +233,25 @@ bool finite_set_decl_plugin::is_value(app * e) const { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         bool is_setop =  | ||||||
|  |             is_app_of(a, m_family_id, OP_FINITE_SET_UNION)  | ||||||
|  |             || is_app_of(a, m_family_id, OP_FINITE_SET_INTERSECT) | ||||||
|  |             || is_app_of(a, m_family_id, OP_FINITE_SET_DIFFERENCE); | ||||||
|         // Check if it's a union
 |         // Check if it's a union
 | ||||||
|         if (is_app_of(a, m_family_id, OP_FINITE_SET_UNION)) { |         if (is_setop) { | ||||||
|             // Add arguments to todo list
 |             // Add arguments to todo list
 | ||||||
|             for (auto arg : *a)  |             for (auto arg : *a)  | ||||||
|                 todo.push_back(arg);             |                 todo.push_back(arg);             | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if (is_app_of(a, m_family_id, OP_FINITE_SET_RANGE)) { | ||||||
|  |             for (auto arg : *a) | ||||||
|  |                 if (!m_manager->is_value(arg)) | ||||||
|  |                     return false; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // can add also ranges where lo and hi are values.
 |         // can add also ranges where lo and hi are values.
 | ||||||
|          |          | ||||||
|         // If it's none of the above, it's not a value
 |         // If it's none of the above, it's not a value
 | ||||||
|  | @ -271,3 +282,10 @@ bool finite_set_decl_plugin::are_distinct(app* e1, app* e2) const { | ||||||
|     // 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.
 | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func_decl *finite_set_util::mk_range_decl() { | ||||||
|  |     arith_util a(m_manager); | ||||||
|  |     sort *i = a.mk_int(); | ||||||
|  |     sort *domain[2] = {i, i}; | ||||||
|  |     return m_manager.mk_func_decl(m_fid, OP_FINITE_SET_RANGE, 0, nullptr, 2, domain, nullptr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -195,6 +195,8 @@ public: | ||||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_FILTER, arr, set); |         return m_manager.mk_app(m_fid, OP_FINITE_SET_FILTER, arr, set); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     func_decl *mk_range_decl(); | ||||||
|  | 
 | ||||||
|     app * mk_range(expr* low, expr* high) { |     app * mk_range(expr* low, expr* high) { | ||||||
|         return m_manager.mk_app(m_fid, OP_FINITE_SET_RANGE, low, high); |         return m_manager.mk_app(m_fid, OP_FINITE_SET_RANGE, low, high); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ Revision History: | ||||||
| --*/ | --*/ | ||||||
| 
 | 
 | ||||||
| #include "ast/ast.h" | #include "ast/ast.h" | ||||||
|  | #include "ast/ast_pp.h" | ||||||
| #include "ast/finite_set_decl_plugin.h" | #include "ast/finite_set_decl_plugin.h" | ||||||
| #include "ast/arith_decl_plugin.h" | #include "ast/arith_decl_plugin.h" | ||||||
| #include "ast/array_decl_plugin.h" | #include "ast/array_decl_plugin.h" | ||||||
|  | @ -38,8 +39,8 @@ void finite_set_axioms::in_empty_axiom(expr *x) { | ||||||
|     expr_ref empty_set(u.mk_empty(elem_sort), m); |     expr_ref empty_set(u.mk_empty(elem_sort), m); | ||||||
|     expr_ref x_in_empty(u.mk_in(x, empty_set), m); |     expr_ref x_in_empty(u.mk_in(x, empty_set), m); | ||||||
|      |      | ||||||
|     theory_axiom ax(m, "in-empty"); |     theory_axiom* ax = alloc(theory_axiom, m, "in-empty"); | ||||||
|     ax.clause.push_back(m.mk_not(x_in_empty)); |     ax->clause.push_back(m.mk_not(x_in_empty)); | ||||||
|     m_add_clause(ax); |     m_add_clause(ax); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -50,27 +51,28 @@ void finite_set_axioms::in_union_axiom(expr *x, expr *a) { | ||||||
|     if (!u.is_union(a, b, c)) |     if (!u.is_union(a, b, c)) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     theory_axiom ax(m, "in-union"); | 
 | ||||||
|     expr_ref x_in_a(u.mk_in(x, a), m); |     expr_ref x_in_a(u.mk_in(x, a), m); | ||||||
|     expr_ref x_in_b(u.mk_in(x, b), m); |     expr_ref x_in_b(u.mk_in(x, b), m); | ||||||
|     expr_ref x_in_c(u.mk_in(x, c), m); |     expr_ref x_in_c(u.mk_in(x, c), m); | ||||||
|      |      | ||||||
|     // (x in a) => (x in b) or (x in c)
 |     // (x in a) => (x in b) or (x in c)
 | ||||||
|     ax.clause.push_back(m.mk_not(x_in_a)); |     theory_axiom *ax1 = alloc(theory_axiom, m, "in-union"); | ||||||
|     ax.clause.push_back(x_in_b); |     ax1->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax.clause.push_back(x_in_c); |     ax1->clause.push_back(x_in_b); | ||||||
|     m_add_clause(ax); |     ax1->clause.push_back(x_in_c); | ||||||
|  |     m_add_clause(ax1); | ||||||
| 
 | 
 | ||||||
|     // (x in b) => (x in a)
 |     // (x in b) => (x in a)
 | ||||||
|     theory_axiom ax2(m, "in-union"); |     theory_axiom* ax2 = alloc(theory_axiom, m, "in-union"); | ||||||
|     ax2.clause.push_back(m.mk_not(x_in_b)); |     ax2->clause.push_back(m.mk_not(x_in_b)); | ||||||
|     ax2.clause.push_back(x_in_a); |     ax2->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| 
 | 
 | ||||||
|     // (x in c) => (x in a)
 |     // (x in c) => (x in a)
 | ||||||
|     theory_axiom ax3(m, "in-union"); |     theory_axiom* ax3 = alloc(theory_axiom, m, "in-union"); | ||||||
|     ax3.clause.push_back(m.mk_not(x_in_c)); |     ax3->clause.push_back(m.mk_not(x_in_c)); | ||||||
|     ax3.clause.push_back(x_in_a); |     ax3->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax3); |     m_add_clause(ax3); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -86,22 +88,22 @@ void finite_set_axioms::in_intersect_axiom(expr *x, expr *a) { | ||||||
|     expr_ref x_in_c(u.mk_in(x, c), m); |     expr_ref x_in_c(u.mk_in(x, c), m); | ||||||
|      |      | ||||||
|     // (x in a) => (x in b)
 |     // (x in a) => (x in b)
 | ||||||
|     theory_axiom ax1(m, "in-intersect"); |     theory_axiom* ax1 = alloc(theory_axiom, m, "in-intersect"); | ||||||
|     ax1.clause.push_back(m.mk_not(x_in_a)); |     ax1->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax1.clause.push_back(x_in_b); |     ax1->clause.push_back(x_in_b); | ||||||
|     m_add_clause(ax1); |     m_add_clause(ax1); | ||||||
| 
 | 
 | ||||||
|     // (x in a) => (x in c)
 |     // (x in a) => (x in c)
 | ||||||
|     theory_axiom ax2(m, "in-intersect"); |     theory_axiom* ax2 = alloc(theory_axiom, m, "in-intersect"); | ||||||
|     ax2.clause.push_back(m.mk_not(x_in_a)); |     ax2->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax2.clause.push_back(x_in_c); |     ax2->clause.push_back(x_in_c); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| 
 | 
 | ||||||
|     // (x in b) and (x in c) => (x in a)
 |     // (x in b) and (x in c) => (x in a)
 | ||||||
|     theory_axiom ax3(m, "in-intersect"); |     theory_axiom* ax3 = alloc(theory_axiom, m, "in-intersect"); | ||||||
|     ax3.clause.push_back(m.mk_not(x_in_b)); |     ax3->clause.push_back(m.mk_not(x_in_b)); | ||||||
|     ax3.clause.push_back(m.mk_not(x_in_c)); |     ax3->clause.push_back(m.mk_not(x_in_c)); | ||||||
|     ax3.clause.push_back(x_in_a); |     ax3->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax3); |     m_add_clause(ax3); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -117,22 +119,22 @@ void finite_set_axioms::in_difference_axiom(expr *x, expr *a) { | ||||||
|     expr_ref x_in_c(u.mk_in(x, c), m); |     expr_ref x_in_c(u.mk_in(x, c), m); | ||||||
|      |      | ||||||
|     // (x in a) => (x in b)
 |     // (x in a) => (x in b)
 | ||||||
|     theory_axiom ax1(m, "in-difference"); |     theory_axiom* ax1 = alloc(theory_axiom, m, "in-difference"); | ||||||
|     ax1.clause.push_back(m.mk_not(x_in_a)); |     ax1->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax1.clause.push_back(x_in_b); |     ax1->clause.push_back(x_in_b); | ||||||
|     m_add_clause(ax1); |     m_add_clause(ax1); | ||||||
|      |      | ||||||
|     // (x in a) => not (x in c)
 |     // (x in a) => not (x in c)
 | ||||||
|     theory_axiom ax2(m, "in-difference"); |     theory_axiom* ax2 = alloc(theory_axiom, m, "in-difference"); | ||||||
|     ax2.clause.push_back(m.mk_not(x_in_a)); |     ax2->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax2.clause.push_back(m.mk_not(x_in_c)); |     ax2->clause.push_back(m.mk_not(x_in_c)); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| 
 | 
 | ||||||
|     // (x in b) and not (x in c) => (x in a)
 |     // (x in b) and not (x in c) => (x in a)
 | ||||||
|     theory_axiom ax3(m, "in-difference"); |     theory_axiom* ax3 = alloc(theory_axiom, m, "in-difference"); | ||||||
|     ax3.clause.push_back(m.mk_not(x_in_b)); |     ax3->clause.push_back(m.mk_not(x_in_b)); | ||||||
|     ax3.clause.push_back(x_in_c); |     ax3->clause.push_back(x_in_c); | ||||||
|     ax3.clause.push_back(x_in_a); |     ax3->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax3); |     m_add_clause(ax3); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -145,11 +147,11 @@ void finite_set_axioms::in_singleton_axiom(expr *x, expr *a) { | ||||||
|      |      | ||||||
|     expr_ref x_in_a(u.mk_in(x, a), m); |     expr_ref x_in_a(u.mk_in(x, a), m); | ||||||
| 
 | 
 | ||||||
|     theory_axiom ax(m, "in-singleton"); |     theory_axiom* ax = alloc(theory_axiom, m, "in-singleton"); | ||||||
|     if (x == b) { |     if (x == b) { | ||||||
|         // If x and b are syntactically identical, then (x in a) is always true
 |         // If x and b are syntactically identical, then (x in a) is always true
 | ||||||
| 
 | 
 | ||||||
|         ax.clause.push_back(x_in_a); |         ax->clause.push_back(x_in_a); | ||||||
|         m_add_clause(ax); |         m_add_clause(ax); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -157,17 +159,42 @@ void finite_set_axioms::in_singleton_axiom(expr *x, expr *a) { | ||||||
|     expr_ref x_eq_b(m.mk_eq(x, b), m); |     expr_ref x_eq_b(m.mk_eq(x, b), m); | ||||||
|      |      | ||||||
|     // (x in a) => (x == b)
 |     // (x in a) => (x == b)
 | ||||||
|     ax.clause.push_back(m.mk_not(x_in_a)); |     ax->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax.clause.push_back(x_eq_b); |     ax->clause.push_back(x_eq_b); | ||||||
|     m_add_clause(ax); |     m_add_clause(ax); | ||||||
|     ax.clause.reset(); |     ax = alloc(theory_axiom, m, "in-singleton"); | ||||||
| 
 | 
 | ||||||
|     // (x == b) => (x in a)
 |     // (x == b) => (x in a)
 | ||||||
|     ax.clause.push_back(m.mk_not(x_eq_b)); |     ax->clause.push_back(m.mk_not(x_eq_b)); | ||||||
|     ax.clause.push_back(x_in_a); |     ax->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax); |     m_add_clause(ax); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void finite_set_axioms::in_singleton_axiom(expr* a) { | ||||||
|  |     expr *b = nullptr; | ||||||
|  |     if (!u.is_singleton(a, b)) | ||||||
|  |         return; | ||||||
|  |      | ||||||
|  |     arith_util arith(m); | ||||||
|  | 
 | ||||||
|  |     expr_ref b_in_a(u.mk_in(b, a), m); | ||||||
|  | 
 | ||||||
|  |     auto ax = alloc(theory_axiom, m, "in-singleton"); | ||||||
|  |     ax->clause.push_back(b_in_a); | ||||||
|  |     m_add_clause(ax); | ||||||
|  | 
 | ||||||
|  |     ax = alloc(theory_axiom, m, "in-singleton"); | ||||||
|  |     expr_ref bm1_in_a(u.mk_in(arith.mk_add(b, arith.mk_int(-1)), a), m); | ||||||
|  |     ax->clause.push_back(m.mk_not(bm1_in_a)); | ||||||
|  |     m_add_clause(ax); | ||||||
|  |      | ||||||
|  |     ax = alloc(theory_axiom, m, "in-singleton"); | ||||||
|  |     expr_ref bp1_in_a(u.mk_in(arith.mk_add(b, arith.mk_int(1)), a), m); | ||||||
|  |     ax->clause.push_back(m.mk_not(bp1_in_a)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| // a := set.range(lo, hi)
 | // a := set.range(lo, hi)
 | ||||||
| // (x in a) <=> (lo <= x <= hi)
 | // (x in a) <=> (lo <= x <= hi)
 | ||||||
| void finite_set_axioms::in_range_axiom(expr *x, expr *a) { | void finite_set_axioms::in_range_axiom(expr *x, expr *a) { | ||||||
|  | @ -177,29 +204,58 @@ void finite_set_axioms::in_range_axiom(expr *x, expr *a) { | ||||||
|      |      | ||||||
|     arith_util arith(m); |     arith_util arith(m); | ||||||
|     expr_ref x_in_a(u.mk_in(x, a), m); |     expr_ref x_in_a(u.mk_in(x, a), m); | ||||||
|     expr_ref lo_le_x(arith.mk_le(lo, x), m); |     expr_ref lo_le_x(arith.mk_le(arith.mk_sub(lo, x), arith.mk_int(0)), m); | ||||||
|     expr_ref x_le_hi(arith.mk_le(x, hi), m); |     expr_ref x_le_hi(arith.mk_le(arith.mk_sub(x, hi), arith.mk_int(0)), m); | ||||||
|  |     m_rewriter(lo_le_x); | ||||||
|  |     m_rewriter(x_le_hi); | ||||||
|      |      | ||||||
|     // (x in a) => (lo <= x)
 |     // (x in a) => (lo <= x)
 | ||||||
|     theory_axiom ax1(m, "in-range"); |     theory_axiom* ax1 = alloc(theory_axiom, m, "in-range"); | ||||||
|     ax1.clause.push_back(m.mk_not(x_in_a)); |     ax1->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax1.clause.push_back(lo_le_x); |     ax1->clause.push_back(lo_le_x); | ||||||
|     m_add_clause(ax1); |     m_add_clause(ax1); | ||||||
| 
 | 
 | ||||||
|     // (x in a) => (x <= hi)
 |     // (x in a) => (x <= hi)
 | ||||||
|     theory_axiom ax2(m, "in-range"); |     theory_axiom* ax2 = alloc(theory_axiom, m, "in-range"); | ||||||
|     ax2.clause.push_back(m.mk_not(x_in_a)); |     ax2->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax2.clause.push_back(x_le_hi); |     ax2->clause.push_back(x_le_hi); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| 
 | 
 | ||||||
|     // (lo <= x) and (x <= hi) => (x in a)
 |     // (lo <= x) and (x <= hi) => (x in a)
 | ||||||
|     theory_axiom ax3(m, "in-range"); |     theory_axiom* ax3 = alloc(theory_axiom, m, "in-range"); | ||||||
|     ax3.clause.push_back(m.mk_not(lo_le_x)); |     ax3->clause.push_back(m.mk_not(lo_le_x)); | ||||||
|     ax3.clause.push_back(m.mk_not(x_le_hi)); |     ax3->clause.push_back(m.mk_not(x_le_hi)); | ||||||
|     ax3.clause.push_back(x_in_a); |     ax3->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax3); |     m_add_clause(ax3); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // a := set.range(lo, hi)
 | ||||||
|  | // (not (set.in (- lo 1) r))
 | ||||||
|  | // (not (set.in (+ hi 1) r))
 | ||||||
|  | // (set.in lo r)
 | ||||||
|  | // (set.in hi r)
 | ||||||
|  | void finite_set_axioms::in_range_axiom(expr* r) { | ||||||
|  |     expr *lo = nullptr, *hi = nullptr; | ||||||
|  |     if (!u.is_range(r, lo, hi)) | ||||||
|  |         return; | ||||||
|  |     theory_axiom* ax = alloc(theory_axiom, m, "range-bounds"); | ||||||
|  |     ax->clause.push_back(u.mk_in(lo, r)); | ||||||
|  |     m_add_clause(ax); | ||||||
|  | 
 | ||||||
|  |     ax = alloc(theory_axiom, m, "range-bounds"); | ||||||
|  |     ax->clause.push_back(u.mk_in(hi, r)); | ||||||
|  |     m_add_clause(ax); | ||||||
|  | 
 | ||||||
|  |     arith_util a(m); | ||||||
|  |     ax = alloc(theory_axiom, m, "range-bounds"); | ||||||
|  |     ax->clause.push_back(m.mk_not(u.mk_in(a.mk_add(hi, a.mk_int(1)), r))); | ||||||
|  |     m_add_clause(ax); | ||||||
|  | 
 | ||||||
|  |     ax = alloc(theory_axiom, m, "range-bounds"); | ||||||
|  |     ax->clause.push_back(m.mk_not(u.mk_in(a.mk_add(lo, a.mk_int(-1)), r))); | ||||||
|  |     m_add_clause(ax); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // a := set.map(f, b)
 | // a := set.map(f, b)
 | ||||||
| // (x in a) <=> set.map_inverse(f, x, b) in b
 | // (x in a) <=> set.map_inverse(f, x, b) in b
 | ||||||
| void finite_set_axioms::in_map_axiom(expr *x, expr *a) { | void finite_set_axioms::in_map_axiom(expr *x, expr *a) { | ||||||
|  | @ -228,9 +284,9 @@ void finite_set_axioms::in_map_image_axiom(expr *x, expr *a) { | ||||||
|     expr_ref fx_in_a(u.mk_in(fx, a), m); |     expr_ref fx_in_a(u.mk_in(fx, a), m); | ||||||
|      |      | ||||||
|     // (x in b) => f(x) in a
 |     // (x in b) => f(x) in a
 | ||||||
|     theory_axiom ax(m, "in-map-image"); |     theory_axiom* ax = alloc(theory_axiom, m, "in-map-image"); | ||||||
|     ax.clause.push_back(m.mk_not(x_in_b)); |     ax->clause.push_back(m.mk_not(x_in_b)); | ||||||
|     ax.clause.push_back(fx_in_a); |     ax->clause.push_back(fx_in_a); | ||||||
|     m_add_clause(ax); |     m_add_clause(ax); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -249,22 +305,22 @@ void finite_set_axioms::in_filter_axiom(expr *x, expr *a) { | ||||||
|     expr_ref px(autil.mk_select(p, x), m); |     expr_ref px(autil.mk_select(p, x), m); | ||||||
|      |      | ||||||
|     // (x in a) => (x in b)
 |     // (x in a) => (x in b)
 | ||||||
|     theory_axiom ax1(m, "in-filter"); |     theory_axiom* ax1 = alloc(theory_axiom, m, "in-filter"); | ||||||
|     ax1.clause.push_back(m.mk_not(x_in_a)); |     ax1->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax1.clause.push_back(x_in_b); |     ax1->clause.push_back(x_in_b); | ||||||
|     m_add_clause(ax1); |     m_add_clause(ax1); | ||||||
| 
 | 
 | ||||||
|     // (x in a) => p(x)
 |     // (x in a) => p(x)
 | ||||||
|     theory_axiom ax2(m, "in-filter"); |     theory_axiom* ax2 = alloc(theory_axiom, m, "in-filter"); | ||||||
|     ax2.clause.push_back(m.mk_not(x_in_a)); |     ax2->clause.push_back(m.mk_not(x_in_a)); | ||||||
|     ax2.clause.push_back(px); |     ax2->clause.push_back(px); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| 
 | 
 | ||||||
|     // (x in b) and p(x) => (x in a)
 |     // (x in b) and p(x) => (x in a)
 | ||||||
|     theory_axiom ax3(m, "in-filter"); |     theory_axiom* ax3 = alloc(theory_axiom, m, "in-filter"); | ||||||
|     ax3.clause.push_back(m.mk_not(x_in_b)); |     ax3->clause.push_back(m.mk_not(x_in_b)); | ||||||
|     ax3.clause.push_back(m.mk_not(px)); |     ax3->clause.push_back(m.mk_not(px)); | ||||||
|     ax3.clause.push_back(x_in_a); |     ax3->clause.push_back(x_in_a); | ||||||
|     m_add_clause(ax3); |     m_add_clause(ax3); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -280,8 +336,8 @@ void finite_set_axioms::size_singleton_axiom(expr *a) { | ||||||
|     expr_ref one(arith.mk_int(1), m); |     expr_ref one(arith.mk_int(1), m); | ||||||
|     expr_ref eq(m.mk_eq(size_a, one), m); |     expr_ref eq(m.mk_eq(size_a, one), m); | ||||||
| 
 | 
 | ||||||
|     theory_axiom ax(m, "size-singleton"); |     theory_axiom* ax = alloc(theory_axiom, m, "size-singleton"); | ||||||
|     ax.clause.push_back(eq); |     ax->clause.push_back(eq); | ||||||
|     m_add_clause(ax); |     m_add_clause(ax); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -293,14 +349,14 @@ void finite_set_axioms::subset_axiom(expr* a) { | ||||||
|     expr_ref intersect_bc(u.mk_intersect(b, c), m); |     expr_ref intersect_bc(u.mk_intersect(b, c), m); | ||||||
|     expr_ref eq(m.mk_eq(intersect_bc, b), m); |     expr_ref eq(m.mk_eq(intersect_bc, b), m); | ||||||
| 
 | 
 | ||||||
|     theory_axiom ax1(m,  "subset"); |     theory_axiom* ax1 = alloc(theory_axiom, m,  "subset"); | ||||||
|     ax1.clause.push_back(m.mk_not(a)); |     ax1->clause.push_back(m.mk_not(a)); | ||||||
|     ax1.clause.push_back(eq); |     ax1->clause.push_back(eq); | ||||||
|     m_add_clause(ax1); |     m_add_clause(ax1); | ||||||
| 
 | 
 | ||||||
|     theory_axiom ax2(m,  "subset"); |     theory_axiom* ax2 = alloc(theory_axiom, m,  "subset"); | ||||||
|     ax2.clause.push_back(a); |     ax2->clause.push_back(a); | ||||||
|     ax2.clause.push_back(m.mk_not(eq)); |     ax2->clause.push_back(m.mk_not(eq)); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -313,15 +369,15 @@ void finite_set_axioms::extensionality_axiom(expr *a, expr* b) { | ||||||
|     expr_ref diff_in_b(u.mk_in(diff_ab, b), m); |     expr_ref diff_in_b(u.mk_in(diff_ab, b), m); | ||||||
|      |      | ||||||
|     // (a != b) => (x in diff_ab != x in diff_ba)
 |     // (a != b) => (x in diff_ab != x in diff_ba)
 | ||||||
|     theory_axiom ax(m, "extensionality"); |     theory_axiom* ax = alloc(theory_axiom, m, "extensionality"); | ||||||
|     ax.clause.push_back(a_eq_b); |     ax->clause.push_back(a_eq_b); | ||||||
|     ax.clause.push_back(m.mk_not(diff_in_a)); |     ax->clause.push_back(m.mk_not(diff_in_a)); | ||||||
|     ax.clause.push_back(m.mk_not(diff_in_b)); |     ax->clause.push_back(m.mk_not(diff_in_b)); | ||||||
|     m_add_clause(ax); |     m_add_clause(ax); | ||||||
| 
 | 
 | ||||||
|     theory_axiom ax2(m, "extensionality"); |     theory_axiom* ax2 = alloc(theory_axiom, m, "extensionality"); | ||||||
|     ax2.clause.push_back(m.mk_not(a_eq_b)); |     ax2->clause.push_back(m.mk_not(a_eq_b)); | ||||||
|     ax2.clause.push_back(diff_in_a); |     ax2->clause.push_back(diff_in_a); | ||||||
|     ax2.clause.push_back(diff_in_b); |     ax2->clause.push_back(diff_in_b); | ||||||
|     m_add_clause(ax2); |     m_add_clause(ax2); | ||||||
| } | } | ||||||
|  | @ -12,6 +12,8 @@ Abstract: | ||||||
|         |         | ||||||
| --*/ | --*/ | ||||||
| 
 | 
 | ||||||
|  | #include "ast/rewriter/th_rewriter.h" | ||||||
|  | 
 | ||||||
| struct theory_axiom { | struct theory_axiom { | ||||||
|     expr_ref_vector   clause; |     expr_ref_vector   clause; | ||||||
|     vector<parameter> params; |     vector<parameter> params; | ||||||
|  | @ -32,14 +34,15 @@ std::ostream &operator<<(std::ostream &out, theory_axiom const &ax); | ||||||
| class finite_set_axioms { | class finite_set_axioms { | ||||||
|     ast_manager&    m; |     ast_manager&    m; | ||||||
|     finite_set_util u; |     finite_set_util u; | ||||||
|  |     th_rewriter m_rewriter; | ||||||
| 
 | 
 | ||||||
|     std::function<void(theory_axiom const &)> m_add_clause; |     std::function<void(theory_axiom *)> m_add_clause; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     finite_set_axioms(ast_manager &m) : m(m), u(m) {} |     finite_set_axioms(ast_manager &m) : m(m), u(m), m_rewriter(m) {} | ||||||
| 
 | 
 | ||||||
|     void set_add_clause(std::function<void(theory_axiom const &)> &ac) { |     void set_add_clause(std::function<void(theory_axiom*)> &ac) { | ||||||
|         m_add_clause = ac; |         m_add_clause = ac; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -62,10 +65,23 @@ public: | ||||||
|     // (x in a) <=> (x == b)
 |     // (x in a) <=> (x == b)
 | ||||||
|     void in_singleton_axiom(expr *x, expr *a); |     void in_singleton_axiom(expr *x, expr *a); | ||||||
| 
 | 
 | ||||||
|  |     // a := set.singleton(b)
 | ||||||
|  |     // b in a
 | ||||||
|  |     // b-1 not in a
 | ||||||
|  |     // b+1 not in a
 | ||||||
|  |     void in_singleton_axiom(expr *a); | ||||||
|  | 
 | ||||||
|     // a := set.range(lo, hi)
 |     // a := set.range(lo, hi)
 | ||||||
|     // (x in a) <=> (lo <= x <= hi)
 |     // (x in a) <=> (lo <= x <= hi)
 | ||||||
|     void in_range_axiom(expr *x, expr *a); |     void in_range_axiom(expr *x, expr *a); | ||||||
| 
 | 
 | ||||||
|  |     // a := set.range(lo, hi)
 | ||||||
|  |     // (not (set.in (- lo 1) a))
 | ||||||
|  |     // (not (set.in (+ hi 1) a))
 | ||||||
|  |     // (set.in lo a)
 | ||||||
|  |     // (set.in hi a)
 | ||||||
|  |     void in_range_axiom(expr *a); | ||||||
|  | 
 | ||||||
|     // a := set.map(f, b)
 |     // a := set.map(f, b)
 | ||||||
|     // (x in a) <=> set.map_inverse(f, x, b) in b
 |     // (x in a) <=> set.map_inverse(f, x, b) in b
 | ||||||
|     void in_map_axiom(expr *x, expr *a); |     void in_map_axiom(expr *x, expr *a); | ||||||
|  |  | ||||||
|  | @ -1525,16 +1525,24 @@ namespace smt { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     lbool context::find_assignment(expr * n) const { |     lbool context::find_assignment(expr * n) const { | ||||||
|         if (m.is_false(n)) | 
 | ||||||
|             return l_false; |  | ||||||
|         expr* arg = nullptr; |         expr* arg = nullptr; | ||||||
|         if (m.is_not(n, arg)) { |         if (m.is_not(n, arg)) { | ||||||
|  | 
 | ||||||
|             if (b_internalized(arg)) |             if (b_internalized(arg)) | ||||||
|                 return ~get_assignment_core(arg); |                 return ~get_assignment_core(arg); | ||||||
|  |             if (m.is_false(arg)) | ||||||
|  |                 return l_true; | ||||||
|  |             if (m.is_true(arg)) | ||||||
|  |                 return l_false; | ||||||
|             return l_undef; |             return l_undef; | ||||||
|         } |         } | ||||||
|         if (b_internalized(n)) |         if (b_internalized(n)) | ||||||
|             return get_assignment(n); |             return get_assignment(n); | ||||||
|  |         if (m.is_false(n)) | ||||||
|  |             return l_false; | ||||||
|  |         if (m.is_true(n)) | ||||||
|  |             return l_true; | ||||||
|         return l_undef; |         return l_undef; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ Abstract: | ||||||
| #include "smt/theory_finite_set.h" | #include "smt/theory_finite_set.h" | ||||||
| #include "smt/smt_context.h" | #include "smt/smt_context.h" | ||||||
| #include "smt/smt_model_generator.h" | #include "smt/smt_model_generator.h" | ||||||
|  | #include "smt/smt_arith_value.h" | ||||||
| #include "ast/ast_pp.h" | #include "ast/ast_pp.h" | ||||||
| 
 | 
 | ||||||
| namespace smt { | namespace smt { | ||||||
|  | @ -29,8 +30,8 @@ namespace smt { | ||||||
|         m_axioms(m), m_find(*this) |         m_axioms(m), m_find(*this) | ||||||
|     { |     { | ||||||
|         // Setup the add_clause callback for axioms
 |         // Setup the add_clause callback for axioms
 | ||||||
|         std::function<void(theory_axiom const &)> add_clause_fn = |         std::function<void(theory_axiom *)> add_clause_fn = | ||||||
|             [this](theory_axiom const &ax) { |             [this](theory_axiom* ax) { | ||||||
|                 this->add_clause(ax); |                 this->add_clause(ax); | ||||||
|             }; |             }; | ||||||
|         m_axioms.set_add_clause(add_clause_fn); |         m_axioms.set_add_clause(add_clause_fn); | ||||||
|  | @ -67,7 +68,7 @@ namespace smt { | ||||||
|         theory_var r = theory::mk_var(n); |         theory_var r = theory::mk_var(n); | ||||||
|         VERIFY(r == static_cast<theory_var>(m_find.mk_var())); |         VERIFY(r == static_cast<theory_var>(m_find.mk_var())); | ||||||
|         SASSERT(r == static_cast<int>(m_var_data.size())); |         SASSERT(r == static_cast<int>(m_var_data.size())); | ||||||
|         m_var_data.push_back(alloc(var_data)); |         m_var_data.push_back(alloc(var_data, m)); | ||||||
|         ctx.push_trail(push_back_vector<ptr_vector<var_data>>(m_var_data)); |         ctx.push_trail(push_back_vector<ptr_vector<var_data>>(m_var_data)); | ||||||
|         ctx.push_trail(new_obj_trail(m_var_data.back())); |         ctx.push_trail(new_obj_trail(m_var_data.back())); | ||||||
|         expr *e = n->get_expr(); |         expr *e = n->get_expr(); | ||||||
|  | @ -90,7 +91,8 @@ namespace smt { | ||||||
|             m_var_data[r]->m_setops.push_back(n); |             m_var_data[r]->m_setops.push_back(n); | ||||||
|             ctx.push_trail(push_back_trail(m_var_data[r]->m_setops)); |             ctx.push_trail(push_back_trail(m_var_data[r]->m_setops)); | ||||||
|             for (auto arg : enode::args(n)) { |             for (auto arg : enode::args(n)) { | ||||||
|                 if (!u.is_finite_set(arg->get_expr())) |                 expr *e = arg->get_expr(); | ||||||
|  |                 if (!u.is_finite_set(e)) | ||||||
|                     continue; |                     continue; | ||||||
|                 auto v = arg->get_root()->get_th_var(get_id()); |                 auto v = arg->get_root()->get_th_var(get_id()); | ||||||
|                 SASSERT(v != null_theory_var); |                 SASSERT(v != null_theory_var); | ||||||
|  | @ -103,6 +105,9 @@ namespace smt { | ||||||
|         } |         } | ||||||
|         else if (u.is_map(e) || u.is_filter(e)) { |         else if (u.is_map(e) || u.is_filter(e)) { | ||||||
|             NOT_IMPLEMENTED_YET(); |             NOT_IMPLEMENTED_YET(); | ||||||
|  |         } | ||||||
|  |         else if (u.is_range(e)) { | ||||||
|  |              | ||||||
|         } |         } | ||||||
|         return r; |         return r; | ||||||
|     } |     } | ||||||
|  | @ -157,6 +162,7 @@ namespace smt { | ||||||
|      *  for each T := (set.op U V) in d2->setops |      *  for each T := (set.op U V) in d2->setops | ||||||
|      *         then S ~ T by construction |      *         then S ~ T by construction | ||||||
|      *              add axioms for (set.in x T) |      *              add axioms for (set.in x T) | ||||||
|  |      *  | ||||||
|     */ |     */ | ||||||
| 
 | 
 | ||||||
|     void theory_finite_set::add_in_axioms(enode *in, var_data *d) { |     void theory_finite_set::add_in_axioms(enode *in, var_data *d) { | ||||||
|  | @ -276,6 +282,9 @@ namespace smt { | ||||||
|         if (activate_unasserted_clause()) |         if (activate_unasserted_clause()) | ||||||
|             return FC_CONTINUE; |             return FC_CONTINUE; | ||||||
| 
 | 
 | ||||||
|  |         if (false && activate_range_local_axioms()) | ||||||
|  |             return FC_CONTINUE; | ||||||
|  | 
 | ||||||
|         if (assume_eqs()) |         if (assume_eqs()) | ||||||
|             return FC_CONTINUE; |             return FC_CONTINUE; | ||||||
|          |          | ||||||
|  | @ -293,20 +302,38 @@ namespace smt { | ||||||
|      * - (set.singleton x) -> (set.in x (set.singleton x)) |      * - (set.singleton x) -> (set.in x (set.singleton x)) | ||||||
|      * - (set.singleton x) -> (set.size (set.singleton x)) = 1 |      * - (set.singleton x) -> (set.size (set.singleton x)) = 1 | ||||||
|      * - (set.empty)       -> (set.size (set.empty)) = 0 |      * - (set.empty)       -> (set.size (set.empty)) = 0 | ||||||
|  |      * - (set.range lo hi) -> lo-1,hi+1 not in range, lo, hi in range | ||||||
|      */ |      */ | ||||||
|     void theory_finite_set::add_immediate_axioms(app* term) { |     void theory_finite_set::add_immediate_axioms(app* term) { | ||||||
|         expr *elem = nullptr, *set = nullptr; |         expr *elem = nullptr, *set = nullptr; | ||||||
|  |         expr *lo = nullptr, *hi = nullptr; | ||||||
|         unsigned sz = m_clauses.axioms.size(); |         unsigned sz = m_clauses.axioms.size(); | ||||||
|         if (u.is_in(term, elem, set) && u.is_empty(set)) |         if (u.is_in(term, elem, set) && u.is_empty(set)) | ||||||
|             add_membership_axioms(elem, set); |             add_membership_axioms(elem, set); | ||||||
|         else if (u.is_subset(term)) |         else if (u.is_subset(term)) | ||||||
|             m_axioms.subset_axiom(term); |             m_axioms.subset_axiom(term); | ||||||
|         else if (u.is_singleton(term, elem)) |         else if (u.is_singleton(term)) | ||||||
|             m_axioms.in_singleton_axiom(elem, term); |             m_axioms.in_singleton_axiom(term); | ||||||
|  |         else if (u.is_range(term, lo, hi)) { | ||||||
|  |             m_axioms.in_range_axiom(term); | ||||||
|  |             auto range = ctx.get_enode(term); | ||||||
|  |             auto v = range->get_th_var(get_id()); | ||||||
|  |             // declare lo-1, lo, hi, hi+1 as range local.
 | ||||||
|  |             // we don't have to add additional range local variables for them.
 | ||||||
|  |             auto &range_local = m_var_data[v]->m_range_local; | ||||||
|  |             ctx.push_trail(push_back_vector(range_local)); | ||||||
|  |             arith_util a(m); | ||||||
|  |             range_local.push_back(lo); | ||||||
|  |             range_local.push_back(hi); | ||||||
|  |             range_local.push_back(a.mk_add(lo, a.mk_int(-1))); | ||||||
|  |             range_local.push_back(a.mk_add(hi, a.mk_int(1))); | ||||||
|  |         } | ||||||
|         |         | ||||||
|         // Assert all new lemmas as clauses
 |         // Assert all new lemmas as clauses
 | ||||||
|         for (unsigned i = sz; i < m_clauses.axioms.size(); ++i) |         for (unsigned i = sz; i < m_clauses.axioms.size(); ++i) { | ||||||
|             m_clauses.squeue.push_back(i); |             m_clauses.squeue.push_back(i); | ||||||
|  |             ctx.push_trail(push_back_vector(m_clauses.squeue)); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void theory_finite_set::assign_eh(bool_var v, bool is_true) { |     void theory_finite_set::assign_eh(bool_var v, bool is_true) { | ||||||
|  | @ -322,8 +349,8 @@ namespace smt { | ||||||
|         for (unsigned i = 0; i < m_clauses.watch[idx].size(); ++i) { |         for (unsigned i = 0; i < m_clauses.watch[idx].size(); ++i) { | ||||||
|             TRACE(finite_set, tout << " watch[" << i << "] size: " << m_clauses.watch[i].size() << "\n";); |             TRACE(finite_set, tout << " watch[" << i << "] size: " << m_clauses.watch[i].size() << "\n";); | ||||||
|             auto clause_idx = m_clauses.watch[idx][i]; |             auto clause_idx = m_clauses.watch[idx][i]; | ||||||
|             auto &ax = m_clauses.axioms[clause_idx]; |             auto* ax = m_clauses.axioms[clause_idx]; | ||||||
|             auto &clause = ax.clause; |             auto &clause = ax->clause; | ||||||
|             if (any_of(clause, [&](expr *lit) { return ctx.find_assignment(lit) == l_true; })) { |             if (any_of(clause, [&](expr *lit) { return ctx.find_assignment(lit) == l_true; })) { | ||||||
|                 TRACE(finite_set, tout << "  satisfied\n";); |                 TRACE(finite_set, tout << "  satisfied\n";); | ||||||
|                 m_clauses.watch[idx][j++] = clause_idx; |                 m_clauses.watch[idx][j++] = clause_idx; | ||||||
|  | @ -365,6 +392,7 @@ namespace smt { | ||||||
|                 continue;  // the clause is removed from this watch list
 |                 continue;  // the clause is removed from this watch list
 | ||||||
|             // either all literals are false, or the other watch literal is propagating.
 |             // either all literals are false, or the other watch literal is propagating.
 | ||||||
|             m_clauses.squeue.push_back(clause_idx); |             m_clauses.squeue.push_back(clause_idx); | ||||||
|  |             ctx.push_trail(push_back_vector(m_clauses.squeue)); | ||||||
|             TRACE(finite_set, tout << "  propagate clause\n";); |             TRACE(finite_set, tout << "  propagate clause\n";); | ||||||
|             m_clauses.watch[idx][j++] = clause_idx; |             m_clauses.watch[idx][j++] = clause_idx; | ||||||
|             ++i; |             ++i; | ||||||
|  | @ -390,21 +418,22 @@ namespace smt { | ||||||
| 
 | 
 | ||||||
|             // empty the propagation queue of clauses to assert
 |             // empty the propagation queue of clauses to assert
 | ||||||
|             while (m_clauses.sqhead < m_clauses.squeue.size() && !ctx.inconsistent()) { |             while (m_clauses.sqhead < m_clauses.squeue.size() && !ctx.inconsistent()) { | ||||||
|                 auto index = m_clauses.squeue[m_clauses.sqhead++]; |                 auto clause_idx = m_clauses.squeue[m_clauses.sqhead++]; | ||||||
|                 auto const &clause = m_clauses.axioms[index]; |                 auto ax = m_clauses.axioms[clause_idx]; | ||||||
|                 assert_clause(clause); |                 assert_clause(ax); | ||||||
|             } |             } | ||||||
|         }       |         }       | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void theory_finite_set::activate_clause(unsigned clause_idx) { |     void theory_finite_set::activate_clause(unsigned clause_idx) { | ||||||
|         TRACE(finite_set, tout << "activate_clause: " << clause_idx << "\n";); |         TRACE(finite_set, tout << "activate_clause: " << clause_idx << "\n";); | ||||||
|         auto &ax = m_clauses.axioms[clause_idx]; |         auto* ax = m_clauses.axioms[clause_idx]; | ||||||
|         auto &clause = ax.clause; |         auto &clause = ax->clause; | ||||||
|         if (any_of(clause, [&](expr *e) { return ctx.find_assignment(e) == l_true; })) |         if (any_of(clause, [&](expr *e) { return ctx.find_assignment(e) == l_true; })) | ||||||
|             return; |             return; | ||||||
|         if (clause.size() <= 1) { |         if (clause.size() <= 1) { | ||||||
|             m_clauses.squeue.push_back(clause_idx); |             m_clauses.squeue.push_back(clause_idx); | ||||||
|  |             ctx.push_trail(push_back_vector(m_clauses.squeue)); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         expr *w1 = nullptr, *w2 = nullptr; |         expr *w1 = nullptr, *w2 = nullptr; | ||||||
|  | @ -430,6 +459,7 @@ namespace smt { | ||||||
|         } |         } | ||||||
|         if (!w2) { |         if (!w2) { | ||||||
|             m_clauses.squeue.push_back(clause_idx); |             m_clauses.squeue.push_back(clause_idx); | ||||||
|  |             ctx.push_trail(push_back_vector(m_clauses.squeue)); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         bool w1neg = m.is_not(w1, w1); |         bool w1neg = m.is_not(w1, w1); | ||||||
|  | @ -446,8 +476,8 @@ namespace smt { | ||||||
|             unsigned index; |             unsigned index; | ||||||
|             unwatch_clause(theory_finite_set &th, unsigned index) : th(th), index(index) {} |             unwatch_clause(theory_finite_set &th, unsigned index) : th(th), index(index) {} | ||||||
|             void undo() override { |             void undo() override { | ||||||
|                 auto &ax = th.m_clauses.axioms[index]; |                 auto* ax = th.m_clauses.axioms[index]; | ||||||
|                 auto &clause = ax.clause; |                 auto &clause = ax->clause; | ||||||
|                 expr *w1 = clause.get(0); |                 expr *w1 = clause.get(0); | ||||||
|                 expr *w2 = clause.get(1); |                 expr *w2 = clause.get(1); | ||||||
|                 bool w1neg = th.m.is_not(w1, w1); |                 bool w1neg = th.m.is_not(w1, w1); | ||||||
|  | @ -487,8 +517,9 @@ namespace smt { | ||||||
|             // Create a union expression that is canonical (sorted)
 |             // Create a union expression that is canonical (sorted)
 | ||||||
|             auto& set = *m_set_members[r]; |             auto& set = *m_set_members[r]; | ||||||
|             ptr_vector<expr> elems; |             ptr_vector<expr> elems; | ||||||
|             for (auto e : set) |             for (auto [e,b] : set) | ||||||
|                 elems.push_back(e->get_expr()); |                 if (b)   | ||||||
|  |                     elems.push_back(e->get_expr()); | ||||||
|             std::sort(elems.begin(), elems.end(), [](expr *a, expr *b) { return a->get_id() < b->get_id(); }); |             std::sort(elems.begin(), elems.end(), [](expr *a, expr *b) { return a->get_id() < b->get_id(); }); | ||||||
|             expr *s = mk_union(elems.size(), elems.data(), n->get_expr()->get_sort()); |             expr *s = mk_union(elems.size(), elems.data(), n->get_expr()->get_sort()); | ||||||
|             trail.push_back(s); |             trail.push_back(s); | ||||||
|  | @ -569,9 +600,10 @@ namespace smt { | ||||||
|         }         |         }         | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void theory_finite_set::add_clause(theory_axiom const& ax) { |     void theory_finite_set::add_clause(theory_axiom* ax) { | ||||||
|         TRACE(finite_set, tout << "add_clause: " << ax << "\n"); |         TRACE(finite_set, tout << "add_clause: " << ax << "\n"); | ||||||
|         ctx.push_trail(push_back_vector(m_clauses.axioms)); |         ctx.push_trail(push_back_vector(m_clauses.axioms)); | ||||||
|  |         ctx.push_trail(new_obj_trail(ax)); | ||||||
|         m_clauses.axioms.push_back(ax); |         m_clauses.axioms.push_back(ax); | ||||||
|         m_stats.m_num_axioms_created++; |         m_stats.m_num_axioms_created++; | ||||||
|     } |     } | ||||||
|  | @ -601,19 +633,19 @@ namespace smt { | ||||||
|         mg.register_factory(m_factory); |         mg.register_factory(m_factory); | ||||||
|         collect_members(); |         collect_members(); | ||||||
|     } |     } | ||||||
| 
 |     | ||||||
|     void theory_finite_set::collect_members() { |     void theory_finite_set::collect_members() { | ||||||
|         // This method can be used to collect all elements that are members of sets
 |         // This method can be used to collect all elements that are members of sets
 | ||||||
|         // and ensure that the model factory has values for them.
 |         // and ensure that the model factory has values for them.
 | ||||||
|         // For now, we rely on the default model construction.
 |         // For now, we rely on the default model construction.
 | ||||||
|         reset_set_members(); |         reset_set_members(); | ||||||
|  | 
 | ||||||
|         for (auto f : m_set_in_decls) { |         for (auto f : m_set_in_decls) { | ||||||
|             for (auto n : ctx.enodes_of(f)) { |             for (auto n : ctx.enodes_of(f)) { | ||||||
|                 SASSERT(u.is_in(n->get_expr())); |                 if (!ctx.is_relevant(n)) | ||||||
|                 auto x = n->get_arg(0); |  | ||||||
|                 if (!ctx.is_relevant(x)) |  | ||||||
|                     continue; |                     continue; | ||||||
|                 x = x->get_root(); |                 SASSERT(u.is_in(n->get_expr())); | ||||||
|  |                 auto x = n->get_arg(0)->get_root(); | ||||||
|                 if (x->is_marked()) |                 if (x->is_marked()) | ||||||
|                     continue; |                     continue; | ||||||
|                 x->set_mark();  // make sure we only do this once per element
 |                 x->set_mark();  // make sure we only do this once per element
 | ||||||
|  | @ -622,61 +654,139 @@ namespace smt { | ||||||
|                         continue; |                         continue; | ||||||
|                     if (!u.is_in(p->get_expr())) |                     if (!u.is_in(p->get_expr())) | ||||||
|                         continue; |                         continue; | ||||||
|                     if (ctx.get_assignment(p->get_expr()) != l_true) |                     bool b = ctx.find_assignment(p->get_expr()) == l_true; | ||||||
|                         continue; |  | ||||||
|                     auto set = p->get_arg(1)->get_root(); |                     auto set = p->get_arg(1)->get_root(); | ||||||
|                     auto elem = p->get_arg(0)->get_root(); |                     auto elem = p->get_arg(0)->get_root(); | ||||||
|                     if (elem != x) |                     if (elem != x) | ||||||
|                         continue; |                         continue; | ||||||
|                     if (!m_set_members.contains(set)) |                     if (!m_set_members.contains(set)) { | ||||||
|                         m_set_members.insert(set, alloc(obj_hashtable<enode>)); |                         using om = obj_map<enode, bool>; | ||||||
|                     m_set_members.find(set)->insert(x); |                         auto m = alloc(om); | ||||||
|  |                         m_set_members.insert(set, m); | ||||||
|  |                     } | ||||||
|  |                     m_set_members.find(set)->insert(x, b); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         for (auto f : m_set_in_decls) { |         for (auto f : m_set_in_decls) { | ||||||
|             for (auto n : ctx.enodes_of(f)) { |             for (auto n : ctx.enodes_of(f)) { | ||||||
|                 SASSERT(u.is_in(n->get_expr())); |                 SASSERT(u.is_in(n->get_expr())); | ||||||
|                 auto x = n->get_arg(0); |                 auto x = n->get_arg(0)->get_root();      | ||||||
|                 x = x->get_root(); |  | ||||||
|                 if (x->is_marked()) |                 if (x->is_marked()) | ||||||
|                     x->unset_mark(); |                     x->unset_mark(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // to collect range interpretations for S:
 | ||||||
|  |     // walk parents of S that are (set.in x S)
 | ||||||
|  |     // evaluate truth value of (set.in x S), evaluate x
 | ||||||
|  |     // add (eval(x), eval(set.in x S)) into a vector.
 | ||||||
|  |     // sort the vector by eval(x)
 | ||||||
|  |     // [(v1, b1), (v2, b2), ..., (vn, bn)]
 | ||||||
|  |     // for the first i, with b_i true, add the range [vi, v_{i+1}-1].
 | ||||||
|  |     // the last bn should be false by construction.
 | ||||||
|  | 
 | ||||||
|  |     void theory_finite_set::add_range_interpretation(enode* s) { | ||||||
|  |         vector<std::tuple<rational, enode *, bool>> elements; | ||||||
|  |         arith_value av(m); | ||||||
|  |         av.init(&ctx); | ||||||
|  |         for (auto p : enode::parents(s)) { | ||||||
|  |             if (!ctx.is_relevant(p)) | ||||||
|  |                 continue; | ||||||
|  |             if (u.is_in(p->get_expr())) { | ||||||
|  |                 rational val; | ||||||
|  |                 auto x = p->get_arg(0)->get_root(); | ||||||
|  |                 VERIFY(av.get_value_equiv(x->get_expr(), val) && val.is_int()); | ||||||
|  |                 elements.push_back({val, x, ctx.find_assignment(p->get_expr()) == l_true}); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         std::stable_sort(elements.begin(), elements.end(), | ||||||
|  |                          [](auto const &a, auto const &b) { return std::get<0>(a) < std::get<0>(b); }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     struct finite_set_value_proc : model_value_proc {     |     struct finite_set_value_proc : model_value_proc {     | ||||||
|         theory_finite_set &th;         |         theory_finite_set &th;         | ||||||
|         sort *s = nullptr; |         enode *n = nullptr; | ||||||
|         obj_hashtable<enode>* m_elements = nullptr; |         obj_map<enode, bool>* m_elements = nullptr; | ||||||
| 
 | 
 | ||||||
|         finite_set_value_proc(theory_finite_set &th, sort *s, obj_hashtable<enode> *elements) |         // use range interpretations if there is a range constraint and the base sort is integer
 | ||||||
|             : th(th), s(s), m_elements(elements) {} |         bool use_range() { | ||||||
|  |             auto &m = th.m; | ||||||
|  |             sort *base_s = nullptr; | ||||||
|  |             VERIFY(th.u.is_finite_set(n->get_expr()->get_sort(), base_s)); | ||||||
|  |             arith_util a(m); | ||||||
|  |             if (!a.is_int(base_s)) | ||||||
|  |                 return false; | ||||||
|  |             func_decl_ref range_fn(th.u.mk_range_decl(), m); | ||||||
|  |             return th.ctx.get_num_enodes_of(range_fn.get()) > 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         finite_set_value_proc(theory_finite_set &th, enode *n, obj_map<enode, bool> *elements) | ||||||
|  |             : th(th), n(n), m_elements(elements) {} | ||||||
| 
 | 
 | ||||||
|         void get_dependencies(buffer<model_value_dependency> &result) override { |         void get_dependencies(buffer<model_value_dependency> &result) override { | ||||||
|             if (!m_elements) |             if (!m_elements) | ||||||
|                 return; |                 return; | ||||||
|             for (auto v : *m_elements) |             bool ur = use_range(); | ||||||
|                 result.push_back(model_value_dependency(v)); |             for (auto [n, b] : *m_elements) | ||||||
|  |                 if (!ur || b) | ||||||
|  |                     result.push_back(model_value_dependency(n)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         app *mk_value(model_generator &mg, expr_ref_vector const &values) override {             |         app *mk_value(model_generator &mg, expr_ref_vector const &values) override {             | ||||||
|             SASSERT(values.empty() == !m_elements); |             SASSERT(values.empty() == !m_elements); | ||||||
|             if (values.empty())  |             auto s = n->get_sort(); | ||||||
|  |             if (values.empty()) | ||||||
|                 return th.u.mk_empty(s); |                 return th.u.mk_empty(s); | ||||||
| 
 | 
 | ||||||
|             SASSERT(m_elements); |             SASSERT(m_elements);             | ||||||
|             SASSERT(values.size() == m_elements->size()); |             if (use_range()) | ||||||
|             return th.mk_union(values.size(), values.data(), s); |                 return mk_range_value(mg, values); | ||||||
|  |             else  | ||||||
|  |                 return th.mk_union(values.size(), values.data(), s); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         app *mk_range_value(model_generator &mg, expr_ref_vector const &values) { | ||||||
|  |             unsigned i = 0; | ||||||
|  |             arith_value av(th.m); | ||||||
|  |             av.init(&th.ctx); | ||||||
|  |             vector<std::tuple<rational, enode *, bool>> elems; | ||||||
|  |             for (auto [n, b] : *m_elements) { | ||||||
|  |                 rational r; | ||||||
|  |                 av.get_value(n->get_expr(), r);                | ||||||
|  |                 elems.push_back({r, n, b}); | ||||||
|  |             } | ||||||
|  |             std::stable_sort(elems.begin(), elems.end(), | ||||||
|  |                              [](auto const &a, auto const &b) { return std::get<0>(a) < std::get<0>(b); }); | ||||||
|  |             app *range = nullptr; | ||||||
|  |             arith_util a(th.m); | ||||||
|  | 
 | ||||||
|  |             for (unsigned j = 0; j < elems.size(); ++j) { | ||||||
|  |                 auto [r, n, b] = elems[j]; | ||||||
|  |                 if (!b) | ||||||
|  |                     continue; | ||||||
|  |                 rational lo = r; | ||||||
|  |                 rational hi = j + 1 < elems.size() ? std::get<0>(elems[j + 1]) - rational(1) : r; | ||||||
|  |                 while (j + 1 < elems.size() && std::get<0>(elems[j + 1]) == hi + rational(1) && std::get<2>(elems[j + 1])) { | ||||||
|  |                     hi = std::get<0>(elems[j + 1]); | ||||||
|  |                     ++j; | ||||||
|  |                 } | ||||||
|  |                 auto new_range = th.u.mk_range(a.mk_int(lo), a.mk_int(hi)); | ||||||
|  |                 range = range ? th.u.mk_union(range, new_range) : new_range; | ||||||
|  |             } | ||||||
|  |             return range ? range : th.u.mk_empty(n->get_sort());         | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     model_value_proc * theory_finite_set::mk_value(enode * n, model_generator & mg) { |     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";);        |         TRACE(finite_set, tout << "mk_value: " << mk_pp(n->get_expr(), m) << "\n";); | ||||||
|         obj_hashtable<enode>*elements = nullptr; |         obj_map<enode, bool>*elements = nullptr; | ||||||
|         sort *s = n->get_expr()->get_sort(); |         n = n->get_root(); | ||||||
|         m_set_members.find(n->get_root(), elements);  |         m_set_members.find(n, elements);  | ||||||
|         return alloc(finite_set_value_proc, *this, s, elements); |         return alloc(finite_set_value_proc, *this, n, elements); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -692,10 +802,62 @@ namespace smt { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool theory_finite_set::assert_clause(theory_axiom const &ax) { |     /*
 | ||||||
|         auto const &clause = ax.clause; |      * Add x-1, x+1 in range axioms for every x in setop(range, S) | ||||||
|  |      * then x-1, x+1 will also propagate against setop(range, S). | ||||||
|  |      */ | ||||||
|  |     bool theory_finite_set::activate_range_local_axioms() { | ||||||
|  |         bool new_axiom = false; | ||||||
|  |         func_decl_ref range_fn(u.mk_range_decl(), m); | ||||||
|  |         for (auto range : ctx.enodes_of(range_fn.get())) { | ||||||
|  |             SASSERT(u.is_range(range->get_expr())); | ||||||
|  |             auto v = range->get_th_var(get_id()); | ||||||
|  |             for (auto p : m_var_data[v]->m_parent_setops) { | ||||||
|  |                 auto w = p->get_th_var(get_id()); | ||||||
|  |                 for (auto in : m_var_data[w]->m_parent_in) { | ||||||
|  |                     if (activate_range_local_axioms(in->get_arg(0)->get_expr(), range)) | ||||||
|  |                         new_axiom = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return new_axiom; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     bool theory_finite_set::activate_range_local_axioms(expr* elem, enode* range) { | ||||||
|  |         auto v = range->get_th_var(get_id()); | ||||||
|  |         auto &range_local = m_var_data[v]->m_range_local; | ||||||
|  |         auto &parent_in = m_var_data[v]->m_parent_in; | ||||||
|  |         if (range_local.contains(elem)) | ||||||
|  |             return false; | ||||||
|  |         arith_util a(m); | ||||||
|  |         expr_ref lo(a.mk_add(elem, a.mk_int(-1)), m); | ||||||
|  |         expr_ref hi(a.mk_add(elem, a.mk_int(1)), m); | ||||||
|  |         bool new_axiom = false; | ||||||
|  |         if (!range_local.contains(lo) && all_of(parent_in, [lo](enode* in) { return in->get_arg(0)->get_expr() != lo; })) { | ||||||
|  |             // lo is not range local and lo is not already in an expression (lo in range)
 | ||||||
|  |             // checking that lo is not in range_local is actually redundant because we will instantiate 
 | ||||||
|  |             // membership expressions for every range local expression.
 | ||||||
|  |             // but we keep this set and check for now in case we want to change the saturation strategy.
 | ||||||
|  |             ctx.push_trail(push_back_vector(range_local)); | ||||||
|  |             range_local.push_back(lo); | ||||||
|  |             m_axioms.in_range_axiom(lo, range->get_expr()); | ||||||
|  |             new_axiom = true; | ||||||
|  |         } | ||||||
|  |         if (!range_local.contains(hi) && | ||||||
|  |             all_of(parent_in, [&hi](enode *in) { return in->get_arg(0)->get_expr() != hi; })) { | ||||||
|  |             ctx.push_trail(push_back_vector(range_local)); | ||||||
|  |             range_local.push_back(hi); | ||||||
|  |             m_axioms.in_range_axiom(hi, range->get_expr()); | ||||||
|  |             new_axiom = true; | ||||||
|  |         } | ||||||
|  |         return new_axiom; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool theory_finite_set::assert_clause(theory_axiom const *ax) { | ||||||
|         expr *unit = nullptr; |         expr *unit = nullptr; | ||||||
|         unsigned undef_count = 0; |         unsigned undef_count = 0; | ||||||
|  |         auto &clause = ax->clause; | ||||||
|         for (auto e : clause) { |         for (auto e : clause) { | ||||||
|             switch (ctx.find_assignment(e)) { |             switch (ctx.find_assignment(e)) { | ||||||
|             case l_true: |             case l_true: | ||||||
|  | @ -719,8 +881,8 @@ namespace smt { | ||||||
|             } |             } | ||||||
|             m_stats.m_num_axioms_propagated++; |             m_stats.m_num_axioms_propagated++; | ||||||
|             enode_pair_vector eqs; |             enode_pair_vector eqs; | ||||||
|             auto just = ext_theory_propagation_justification(get_id(), ctx, antecedent.size(), antecedent.data(), eqs.size(), eqs.data(), lit, ax.params.size(), |             auto just = ext_theory_propagation_justification(get_id(), ctx, antecedent.size(), antecedent.data(), eqs.size(), eqs.data(),  | ||||||
|                                                        ax.params.data()); |                 lit, ax->params.size(), ax->params.data()); | ||||||
|             auto bjust = ctx.mk_justification(just); |             auto bjust = ctx.mk_justification(just); | ||||||
|             if (ctx.clause_proof_active()) { |             if (ctx.clause_proof_active()) { | ||||||
|                 // assume all justifications is a non-empty list of symbol parameters
 |                 // assume all justifications is a non-empty list of symbol parameters
 | ||||||
|  | @ -729,8 +891,8 @@ namespace smt { | ||||||
|                 // this misses conflicts at base level.
 |                 // this misses conflicts at base level.
 | ||||||
|                 proof_ref pr(m); |                 proof_ref pr(m); | ||||||
|                 expr_ref_vector args(m); |                 expr_ref_vector args(m); | ||||||
|                 for (auto const& p : ax.params) |                 for (auto const& p : ax->params) | ||||||
|                     args.push_back(m.mk_const(p.get_symbol(), m.mk_proof_sort()));                   |                     args.push_back(m.mk_const(p.get_symbol(), m.mk_proof_sort())); | ||||||
|                 pr = m.mk_app(m.get_family_name(get_family_id()), args.size(), args.data(), m.mk_proof_sort()); |                 pr = m.mk_app(m.get_family_name(get_family_id()), args.size(), args.data(), m.mk_proof_sort()); | ||||||
|                 justification_proof_wrapper jp(ctx, pr.get(), false); |                 justification_proof_wrapper jp(ctx, pr.get(), false); | ||||||
|                 ctx.get_clause_proof().propagate(lit, &jp, antecedent); |                 ctx.get_clause_proof().propagate(lit, &jp, antecedent); | ||||||
|  | @ -748,7 +910,7 @@ namespace smt { | ||||||
|         literal_vector lclause; |         literal_vector lclause; | ||||||
|         for (auto e : clause) |         for (auto e : clause) | ||||||
|             lclause.push_back(mk_literal(e)); |             lclause.push_back(mk_literal(e)); | ||||||
|         ctx.mk_th_axiom(get_id(), lclause, ax.params.size(), ax.params.data()); |         ctx.mk_th_axiom(get_id(), lclause, ax->params.size(), ax->params.data()); | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -101,13 +101,15 @@ namespace smt { | ||||||
|         friend struct finite_set_value_proc; |         friend struct finite_set_value_proc; | ||||||
| 
 | 
 | ||||||
|         struct var_data { |         struct var_data { | ||||||
|             ptr_vector<enode> m_setops; |             ptr_vector<enode> m_setops;                // set operations equivalent to this
 | ||||||
|             ptr_vector<enode> m_parent_in; |             ptr_vector<enode> m_parent_in;             // x in A expressions
 | ||||||
|             ptr_vector<enode> m_parent_setops; |             ptr_vector<enode> m_parent_setops;         // set of set expressions where this appears as sub-expression
 | ||||||
|  |             expr_ref_vector   m_range_local;           // set of range local variables associated with range
 | ||||||
|  |             var_data(ast_manager &m) : m_range_local(m) {} | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         struct theory_clauses { |         struct theory_clauses { | ||||||
|             vector<theory_axiom>    axioms;            // vector of created theory axioms
 |             ptr_vector<theory_axiom>    axioms;            // vector of created theory axioms
 | ||||||
|             unsigned                aqhead = 0;        // queue head of created axioms
 |             unsigned                aqhead = 0;        // queue head of created axioms
 | ||||||
|             unsigned_vector         squeue;            // propagation queue of axioms to be added to the solver
 |             unsigned_vector         squeue;            // propagation queue of axioms to be added to the solver
 | ||||||
|             unsigned                sqhead = 0;        // head into propagation queue axioms to be added to solver
 |             unsigned                sqhead = 0;        // head into propagation queue axioms to be added to solver
 | ||||||
|  | @ -133,12 +135,16 @@ namespace smt { | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  |         struct range { | ||||||
|  |             rational lo, hi; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|         finite_set_util           u; |         finite_set_util           u; | ||||||
|         finite_set_axioms         m_axioms; |         finite_set_axioms         m_axioms; | ||||||
|         th_union_find             m_find; |         th_union_find             m_find; | ||||||
|         theory_clauses            m_clauses; |         theory_clauses            m_clauses; | ||||||
|         finite_set_factory *m_factory = nullptr; |         finite_set_factory *m_factory = nullptr; | ||||||
|         obj_map<enode, obj_hashtable<enode> *> m_set_members; |         obj_map<enode, obj_map<enode, bool> *> m_set_members; | ||||||
|         ptr_vector<func_decl> m_set_in_decls; |         ptr_vector<func_decl> m_set_in_decls; | ||||||
|         ptr_vector<var_data> m_var_data; |         ptr_vector<var_data> m_var_data; | ||||||
|         stats m_stats; |         stats m_stats; | ||||||
|  | @ -172,11 +178,13 @@ namespace smt { | ||||||
| 
 | 
 | ||||||
|         // Helper methods for axiom instantiation
 |         // Helper methods for axiom instantiation
 | ||||||
|         void add_membership_axioms(expr* elem, expr* set); |         void add_membership_axioms(expr* elem, expr* set); | ||||||
|         void add_clause(theory_axiom const& ax); |         void add_clause(theory_axiom * ax); | ||||||
|         bool assert_clause(theory_axiom const &ax); |         bool assert_clause(theory_axiom const *ax); | ||||||
|         void activate_clause(unsigned index); |         void activate_clause(unsigned index); | ||||||
|         bool activate_unasserted_clause(); |         bool activate_unasserted_clause(); | ||||||
|         void add_immediate_axioms(app *atom); |         void add_immediate_axioms(app *atom); | ||||||
|  |         bool activate_range_local_axioms(); | ||||||
|  |         bool activate_range_local_axioms(expr *elem, enode *range); | ||||||
|         bool assume_eqs(); |         bool assume_eqs(); | ||||||
|         bool is_new_axiom(expr *a, expr *b); |         bool is_new_axiom(expr *a, expr *b); | ||||||
|         app *mk_union(unsigned num_elems, expr *const *elems, sort* set_sort); |         app *mk_union(unsigned num_elems, expr *const *elems, sort* set_sort); | ||||||
|  | @ -184,6 +192,7 @@ namespace smt { | ||||||
|         // model construction
 |         // model construction
 | ||||||
|         void collect_members(); |         void collect_members(); | ||||||
|         void reset_set_members(); |         void reset_set_members(); | ||||||
|  |         void add_range_interpretation(enode *s); | ||||||
| 
 | 
 | ||||||
|         // manage union-find of theory variables
 |         // manage union-find of theory variables
 | ||||||
|         theory_var find(theory_var v) const { return m_find.find(v); }         |         theory_var find(theory_var v) const { return m_find.find(v); }         | ||||||
|  |  | ||||||
|  | @ -130,14 +130,12 @@ static void tst_finite_set_map_filter() { | ||||||
| static void tst_finite_set_is_value() { | static void tst_finite_set_is_value() { | ||||||
|     ast_manager m; |     ast_manager m; | ||||||
|     reg_decl_plugins(m); |     reg_decl_plugins(m); | ||||||
|      |          | ||||||
| 
 |  | ||||||
|      |  | ||||||
|     finite_set_util fsets(m); |     finite_set_util fsets(m); | ||||||
|     arith_util arith(m); |     arith_util arith(m); | ||||||
|     finite_set_decl_plugin* plugin = static_cast<finite_set_decl_plugin*>(m.get_plugin(fsets.get_family_id())); |     finite_set_decl_plugin* plugin = static_cast<finite_set_decl_plugin*>(m.get_plugin(fsets.get_family_id())); | ||||||
|    |    | ||||||
|       // Create Int sort and finite set sort
 |     // Create Int sort and finite set sort
 | ||||||
|      |      | ||||||
|     // Test with Int sort (should be fully interpreted)
 |     // Test with Int sort (should be fully interpreted)
 | ||||||
|     sort_ref int_sort(arith.mk_int(), m); |     sort_ref int_sort(arith.mk_int(), m); | ||||||
|  | @ -145,7 +143,7 @@ static void tst_finite_set_is_value() { | ||||||
|     sort_ref finite_set_int(m.mk_sort(fsets.get_family_id(), FINITE_SET_SORT, 1, &int_param), m); |     sort_ref finite_set_int(m.mk_sort(fsets.get_family_id(), FINITE_SET_SORT, 1, &int_param), m); | ||||||
|      |      | ||||||
|      |      | ||||||
|   // Test 1: Empty set is a value
 |     // Test 1: Empty set is a value
 | ||||||
|     app_ref empty_set(fsets.mk_empty(finite_set_int), m); |     app_ref empty_set(fsets.mk_empty(finite_set_int), m); | ||||||
|     ENSURE(plugin->is_value(empty_set.get())); |     ENSURE(plugin->is_value(empty_set.get())); | ||||||
|      |      | ||||||
|  | @ -179,19 +177,19 @@ static void tst_finite_set_is_value() { | ||||||
|     app_ref union_triple(fsets.mk_union(union_temp, singleton_nine), m); |     app_ref union_triple(fsets.mk_union(union_temp, singleton_nine), m); | ||||||
|     ENSURE(plugin->is_value(union_triple.get())); |     ENSURE(plugin->is_value(union_triple.get())); | ||||||
|      |      | ||||||
|     // Test 8: Range is NOT a value (it's not a union of empty/singletons)
 |     // Test 8: Range is a value
 | ||||||
|     expr_ref zero(arith.mk_int(0), m); |     expr_ref zero(arith.mk_int(0), m); | ||||||
|     expr_ref ten(arith.mk_int(10), m); |     expr_ref ten(arith.mk_int(10), m); | ||||||
|     app_ref range_set(fsets.mk_range(zero, ten), m); |     app_ref range_set(fsets.mk_range(zero, ten), m); | ||||||
|     ENSURE(!plugin->is_value(range_set.get())); |     ENSURE(plugin->is_value(range_set.get())); | ||||||
|      |      | ||||||
|     // Test 9: Union with range is NOT a value
 |     // Test 9: Union with range is  a value
 | ||||||
|     app_ref union_with_range(fsets.mk_union(singleton_five, range_set), m); |     app_ref union_with_range(fsets.mk_union(singleton_five, range_set), m); | ||||||
|     ENSURE(!plugin->is_value(union_with_range.get())); |     ENSURE(plugin->is_value(union_with_range.get())); | ||||||
|      |      | ||||||
|     // Test 10: Intersect is NOT a value
 |     // Test 10: Intersect is a value
 | ||||||
|     app_ref intersect_set(fsets.mk_intersect(singleton_five, singleton_seven), m); |     app_ref intersect_set(fsets.mk_intersect(singleton_five, singleton_seven), m); | ||||||
|     ENSURE(!plugin->is_value(intersect_set.get())); |     ENSURE(plugin->is_value(intersect_set.get())); | ||||||
|     ENSURE(m.is_fully_interp(int_sort)); |     ENSURE(m.is_fully_interp(int_sort)); | ||||||
|     ENSURE(m.is_fully_interp(finite_set_int)); |     ENSURE(m.is_fully_interp(finite_set_int)); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue