mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	Setting up param tuning python prototyping experiment (#7993)
* draft attempt at optimizing cube tree with resolvents. have not tested/ran yet
* adding comments
* fix bug about needing to bubble resolvent upwards to highest ancestor
* fix bug where we need to cover the whole resolvent in the path when bubbling up
* clean up comments
* Bump actions/checkout from 4 to 5 (#7954)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)
---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* close entire tree when sibling resolvent is empty
* integrate asms directly into cube tree, remove separate tracking
* try to fix bug about redundant resolutions, merging close and try_resolve_upwards into once function
* separate the logic again to avoid mutual recursion
* [WIP] Add a mutex to warning.cpp to ensure that warning messages from different threads don't interfere (#7963)
* Initial plan
* Add mutex to warning.cpp for thread safety
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Remove unused variable 'first' in mpz.cpp
Removed unused variable 'first' from the function.
* fixing the order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* fixing the order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* fix the order of parameter evaluation
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* remove AI slop
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* param order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* param order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* param order evaluation
* parameter eval order
* parameter evaluation order
* param eval
* param eval order
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* parameter eval order
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* Bump github/codeql-action from 3 to 4 (#7971)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)
---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Update Z3_mk_datatype_sort API to accept array of sort parameters and add Z3_mk_polymorphic_datatype (#7966)
* Initial plan
* Update Z3_mk_datatype_sort API to accept array of parameters
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Update language bindings for parametric datatype sort API
- Python: Updated DatatypeSort() to accept optional params list
- OCaml: Added mk_sort_ref_p/mk_sort_ref_ps for parametric datatypes
- .NET: Added MkDatatypeSortRef() methods with optional params
- Java: Added mkDatatypeSortRef() methods with optional params
- All changes maintain backward compatibility
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Fix .NET build error - rename params to parameters
The 'params' keyword is reserved in C#, causing compilation errors.
Renamed parameter from 'params' to 'parameters' in MkDatatypeSortRef methods.
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Add unit test for parametric datatypes
Added test_parametric_datatype.cpp to demonstrate polymorphic datatypes.
The test creates two concrete instantiations of a generic pair concept:
- pair_int_real with fields (first:Int, second:Real)
- pair_real_int with fields (first:Real, second:Int)
Then verifies that accessors work correctly and type checking is enforced.
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Implement polymorphic type variables support for parametric datatypes
- Modified datatype_decl_plugin to allow type variables as parameters
- Updated mk_datatype_decl to extract type variables from field sorts
- Type variables are collected in order of first appearance
- Revised unit test to use Z3_mk_type_variable for polymorphic datatypes
- Test creates pair datatype with type variables alpha and beta
- Successfully instantiates with concrete types (pair Int Real) and (pair Real Int)
- Verifies accessor types match and equality terms are well-typed
- All tests pass
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Add Z3_mk_polymorphic_datatype API and refactor datatype creation
- Added new API Z3_mk_polymorphic_datatype to z3_api.h
- Renamed static mk_datatype_decl to api_datatype_decl in api_datatype.cpp
- Modified api_datatype_decl to accept explicit type parameters
- Updated all callers to use renamed function
- Added test_polymorphic_datatype_api demonstrating new API usage
- Both tests pass successfully
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Remove type variable collection logic from constructors
Removed the logic for collecting type variables from field sorts based on constructors.
* Update comments on parameter handling in api_datatype.cpp
Clarify usage of parameters in API documentation.
* Fix OCaml build error - use list instead of array for mk_datatype_sort
Changed mk_sort_ref to pass empty list [] instead of empty array [||].
Changed mk_sort_ref_p to pass params list directly instead of converting to array.
Z3native.mk_datatype_sort expects a list, not an array.
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Add polymorphic datatype example to C++ examples
Added polymorphic_datatype_example() demonstrating:
- Creating type variables alpha and beta with Z3_mk_type_variable
- Defining parametric Pair datatype with fields of type alpha and beta
- Instantiating with concrete types (Pair Int Real) and (Pair Real Int)
- Getting constructors and accessors from instantiated datatypes
- Creating constants and expressions using the polymorphic types
- Verifying type correctness with equality (= (first p1) (second p2))
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* trim parametric datatype test
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* restore single cell
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* restore the method behavior
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
* setting up python tuning experiment, not done
* Add finite_set_value_factory for creating finite set values in model generation (#7981)
* Initial plan
* Add finite_set_value_factory implementation
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Remove unused dl_decl_plugin variable and include
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Update copyright and add TODOs in finite_set_value_factory
Updated copyright information and added TODO comments for handling in finite_set_value_factory methods.
* Update copyright information in finite_set_value_factory.h
Updated copyright year from 2006 to 2025.
* Implement finite_set_value_factory using array_util to create singleton sets
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Simplify empty set creation in finite_set_value_factory
Refactor finite_set_value_factory to simplify empty set handling and remove array-specific logic.
* Change family ID for finite_set_value_factory
* Fix build error by restoring array_decl_plugin include and implementation
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Update finite_set_value_factory.h
* Add SASSERT for finite set check in factory
Added assertion to check if the sort is a finite set.
* Rename member variable from m_util to u
* Refactor finite_set_value_factory for value handling
* Use register_value instead of direct set insertion
Replaced direct insertion into set with register_value calls.
* Update finite_set_value_factory.cpp
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* Revert "Add finite_set_value_factory for creating finite set values in model …" (#7985)
This reverts commit 05ffc0a77b.
* Update arith_rewriter.cpp
fix memory leak introduced by update to ensure determinism
* update pythonnn prototyping experiment, need to add a couple more things
* add explicit constructors for nightly mac build failure
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* build fixes
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* fixes
* fix some more things but now it hangs
* change multithread to multiprocess seems to have resolved current deadlock
* fix some bugs, it seems to run now
* fix logic about checking clauses individually, and add proof prefix clause selection (naively) via the OnClause hook
* disable manylinux until segfault is resolved
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
* add the  "noexcept" keyword to value_score=(value_score&&) declaration
* expose a status flag for clauses but every single one is being coded as an assumption...
* Add a fast-path to _coerce_exprs. (#7995)
When the inputs are already the same sort, we can skip most of the
coercion logic and just return.
Currently, `_coerce_exprs` is by far the most expensive part of
building up many common Z3 ASTs, so this fast-path is a substantial
speedup for many use-cases.
* Bump actions/setup-node from 5 to 6 (#7994)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)
---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Co-authored-by: Lev Nachmanson <levnach@hotmail.com>
Co-authored-by: Nelson Elhage <nelhage@nelhage.com>
			
			
This commit is contained in:
		
							parent
							
								
									c5d65cdedd
								
							
						
					
					
						commit
						a4bcd74ba5
					
				
					 33 changed files with 989 additions and 118 deletions
				
			
		
							
								
								
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -20,18 +20,18 @@ jobs: | |||
| 
 | ||||
|     steps: | ||||
|     - name: Checkout repository | ||||
|       uses: actions/checkout@v4 | ||||
|       uses: actions/checkout@v5 | ||||
| 
 | ||||
|     - name: Initialize CodeQL | ||||
|       uses: github/codeql-action/init@v3 | ||||
|       uses: github/codeql-action/init@v4 | ||||
|       with: | ||||
|         languages: ${{ matrix.language }} | ||||
| 
 | ||||
|     - name: Autobuild | ||||
|       uses: github/codeql-action/autobuild@v3 | ||||
|       uses: github/codeql-action/autobuild@v4 | ||||
| 
 | ||||
|     - name: Run CodeQL Query | ||||
|       uses: github/codeql-action/analyze@v3 | ||||
|       uses: github/codeql-action/analyze@v4 | ||||
|       with: | ||||
|         category: 'custom' | ||||
|         queries: ./codeql/custom-queries | ||||
							
								
								
									
										2
									
								
								.github/workflows/wasm-release.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/wasm-release.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -24,7 +24,7 @@ jobs: | |||
|         uses: actions/checkout@v5 | ||||
| 
 | ||||
|       - name: Setup node | ||||
|         uses: actions/setup-node@v5 | ||||
|         uses: actions/setup-node@v6 | ||||
|         with: | ||||
|           node-version: "lts/*" | ||||
|           registry-url: "https://registry.npmjs.org" | ||||
|  |  | |||
							
								
								
									
										2
									
								
								.github/workflows/wasm.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/wasm.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -24,7 +24,7 @@ jobs: | |||
|         uses: actions/checkout@v5 | ||||
| 
 | ||||
|       - name: Setup node | ||||
|         uses: actions/setup-node@v5 | ||||
|         uses: actions/setup-node@v6 | ||||
|         with: | ||||
|           node-version: "lts/*" | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								a-tst.gcno
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								a-tst.gcno
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -66,6 +66,7 @@ jobs: | |||
|   pool: | ||||
|     vmImage: "ubuntu-latest" | ||||
|   container: "quay.io/pypa/manylinux2014_x86_64:latest" | ||||
|   condition: eq(0,1)  | ||||
|   steps: | ||||
|   - script: curl -L -o /tmp/arm-toolchain.tar.xz 'https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz?rev=33c6e30e5ac64e6dba8f0431f2c35f1b&hash=9918A05BF47621B632C7A5C8D2BB438FB80A4480' | ||||
|   - script: mkdir -p /tmp/arm-toolchain/ | ||||
|  |  | |||
|  | @ -1006,6 +1006,95 @@ void datatype_example() { | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| void polymorphic_datatype_example() { | ||||
|     std::cout << "polymorphic datatype example\n"; | ||||
|     context ctx; | ||||
| 
 | ||||
|     // Create type variables alpha and beta for polymorphic datatype using C API
 | ||||
|     Z3_symbol alpha_sym = Z3_mk_string_symbol(ctx, "alpha"); | ||||
|     Z3_symbol beta_sym = Z3_mk_string_symbol(ctx, "beta"); | ||||
|     sort alpha(ctx, Z3_mk_type_variable(ctx, alpha_sym)); | ||||
|     sort beta(ctx, Z3_mk_type_variable(ctx, beta_sym)); | ||||
|      | ||||
|     std::cout << "Type variables: " << alpha << ", " << beta << "\n"; | ||||
| 
 | ||||
|     // Define parametric Pair datatype with constructor mk-pair(first: alpha, second: beta)
 | ||||
|     symbol pair_name = ctx.str_symbol("Pair"); | ||||
|     symbol mk_pair_name = ctx.str_symbol("mk-pair"); | ||||
|     symbol is_pair_name = ctx.str_symbol("is-pair"); | ||||
|     symbol first_name = ctx.str_symbol("first"); | ||||
|     symbol second_name = ctx.str_symbol("second"); | ||||
|      | ||||
|     symbol field_names[2] = {first_name, second_name}; | ||||
|     sort field_sorts[2] = {alpha, beta};  // Use type variables
 | ||||
|      | ||||
|     constructors cs(ctx); | ||||
|     cs.add(mk_pair_name, is_pair_name, 2, field_names, field_sorts); | ||||
|     sort pair = ctx.datatype(pair_name, cs); | ||||
|      | ||||
|     std::cout << "Created parametric datatype: " << pair << "\n"; | ||||
|      | ||||
|     // Instantiate Pair with concrete types: (Pair Int Real)
 | ||||
|     sort_vector params_int_real(ctx); | ||||
|     params_int_real.push_back(ctx.int_sort()); | ||||
|     params_int_real.push_back(ctx.real_sort()); | ||||
|     sort pair_int_real = ctx.datatype_sort(pair_name, params_int_real); | ||||
|      | ||||
|     std::cout << "Instantiated with Int and Real: " << pair_int_real << "\n"; | ||||
|      | ||||
|     // Instantiate Pair with concrete types: (Pair Real Int)
 | ||||
|     sort_vector params_real_int(ctx); | ||||
|     params_real_int.push_back(ctx.real_sort()); | ||||
|     params_real_int.push_back(ctx.int_sort()); | ||||
|     sort pair_real_int = ctx.datatype_sort(pair_name, params_real_int); | ||||
|      | ||||
|     std::cout << "Instantiated with Real and Int: " << pair_real_int << "\n"; | ||||
|      | ||||
|     // Get constructors and accessors for (Pair Int Real) using C API
 | ||||
|     func_decl mk_pair_ir(ctx, Z3_get_datatype_sort_constructor(ctx, pair_int_real, 0)); | ||||
|     func_decl first_ir(ctx, Z3_get_datatype_sort_constructor_accessor(ctx, pair_int_real, 0, 0)); | ||||
|     func_decl second_ir(ctx, Z3_get_datatype_sort_constructor_accessor(ctx, pair_int_real, 0, 1)); | ||||
|      | ||||
|     std::cout << "Constructors and accessors for (Pair Int Real):\n"; | ||||
|     std::cout << "  Constructor: " << mk_pair_ir << "\n"; | ||||
|     std::cout << "  first accessor: " << first_ir << "\n"; | ||||
|     std::cout << "  second accessor: " << second_ir << "\n"; | ||||
|      | ||||
|     // Get constructors and accessors for (Pair Real Int) using C API
 | ||||
|     func_decl mk_pair_ri(ctx, Z3_get_datatype_sort_constructor(ctx, pair_real_int, 0)); | ||||
|     func_decl first_ri(ctx, Z3_get_datatype_sort_constructor_accessor(ctx, pair_real_int, 0, 0)); | ||||
|     func_decl second_ri(ctx, Z3_get_datatype_sort_constructor_accessor(ctx, pair_real_int, 0, 1)); | ||||
|      | ||||
|     std::cout << "Constructors and accessors for (Pair Real Int):\n"; | ||||
|     std::cout << "  Constructor: " << mk_pair_ri << "\n"; | ||||
|     std::cout << "  first accessor: " << first_ri << "\n"; | ||||
|     std::cout << "  second accessor: " << second_ri << "\n"; | ||||
|      | ||||
|     // Create constants of these types
 | ||||
|     expr p1 = ctx.constant("p1", pair_int_real); | ||||
|     expr p2 = ctx.constant("p2", pair_real_int); | ||||
|      | ||||
|     std::cout << "Created constants: " << p1 << " : " << p1.get_sort() << "\n"; | ||||
|     std::cout << "                   " << p2 << " : " << p2.get_sort() << "\n"; | ||||
|      | ||||
|     // Create expressions using accessors
 | ||||
|     expr first_p1 = first_ir(p1);   // first(p1) has type Int
 | ||||
|     expr second_p2 = second_ri(p2); // second(p2) has type Int
 | ||||
|      | ||||
|     std::cout << "first(p1) = " << first_p1 << " : " << first_p1.get_sort() << "\n"; | ||||
|     std::cout << "second(p2) = " << second_p2 << " : " << second_p2.get_sort() << "\n"; | ||||
|      | ||||
|     // Create equality term: (= (first p1) (second p2))
 | ||||
|     expr eq = first_p1 == second_p2; | ||||
|     std::cout << "Equality term: " << eq << "\n"; | ||||
|      | ||||
|     // Verify both sides have the same type (Int)
 | ||||
|     assert(first_p1.get_sort().id() == ctx.int_sort().id()); | ||||
|     assert(second_p2.get_sort().id() == ctx.int_sort().id()); | ||||
|      | ||||
|     std::cout << "Successfully created and verified polymorphic datatypes!\n"; | ||||
| } | ||||
| 
 | ||||
| void expr_vector_example() { | ||||
|     std::cout << "expr_vector example\n"; | ||||
|     context c; | ||||
|  | @ -1394,6 +1483,7 @@ int main() { | |||
|         enum_sort_example(); std::cout << "\n"; | ||||
|         tuple_example(); std::cout << "\n"; | ||||
|         datatype_example(); std::cout << "\n"; | ||||
|         polymorphic_datatype_example(); std::cout << "\n"; | ||||
|         expr_vector_example(); std::cout << "\n"; | ||||
|         exists_expr_vector_example(); std::cout << "\n"; | ||||
|         substitute_example(); std::cout << "\n"; | ||||
|  |  | |||
							
								
								
									
										254
									
								
								param-tuning-experiment.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								param-tuning-experiment.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,254 @@ | |||
| from multiprocessing import Process | ||||
| import math, random | ||||
| 
 | ||||
| import sys, os | ||||
| sys.path.insert(0, os.path.abspath("build/python")) | ||||
| os.environ["Z3_LIBRARY_PATH"] = os.path.abspath("build") | ||||
| 
 | ||||
| # import z3 | ||||
| # print("Using z3 from:", z3.__file__) | ||||
| 
 | ||||
| from z3 import * | ||||
| 
 | ||||
| MAX_CONFLICTS = 100 | ||||
| MAX_EXAMPLES = 5 | ||||
| bench_dir = "../z3-poly-testing/inputs/QF_NIA_small" | ||||
| 
 | ||||
| BASE_PARAM_CANDIDATES = [ | ||||
|     ("smt.arith.eager_eq_axioms", False), | ||||
|     ("smt.restart_factor", 1.2), | ||||
|     ("smt.relevancy", 0), | ||||
|     ("smt.phase_caching_off", 200), | ||||
|     ("smt.phase_caching_on", 600), | ||||
| ] | ||||
| 
 | ||||
| # -------------------------- | ||||
| # One class: BatchManager | ||||
| # -------------------------- | ||||
| class BatchManager: | ||||
|     def __init__(self): | ||||
|         self.best_param_state = None | ||||
|         self.best_score = (math.inf, math.inf, math.inf) | ||||
|         self.search_complete = False | ||||
| 
 | ||||
|     def mark_complete(self): | ||||
|         self.search_complete = True | ||||
| 
 | ||||
|     def maybe_update_best(self, param_state, triple): | ||||
|         if self._better(triple, self.best_score): | ||||
|             self.best_param_state = list(param_state) | ||||
|             self.best_score = triple | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def _better(a, b): | ||||
|         return a < b  # lexicographic compare | ||||
| 
 | ||||
| 
 | ||||
| # ------------------- | ||||
| # Helpers | ||||
| # ------------------- | ||||
| 
 | ||||
| def solver_from_file(filepath): | ||||
|     s = Solver() | ||||
|     s.set("smt.auto_config", False) | ||||
|     s.from_file(filepath) | ||||
|     return s | ||||
| 
 | ||||
| 
 | ||||
| def apply_param_state(s, param_state): | ||||
|     print(f"Applying param state: {param_state}") | ||||
|     for name, value in param_state: | ||||
|         s.set(name, value) | ||||
| 
 | ||||
| 
 | ||||
| def stats_tuple(st): | ||||
|     def get(key): | ||||
|         return int(st.get_key_value(key)) if key in st.keys() else 0 | ||||
|     return (get("conflicts"), get("decisions"), get("rlimit count")) | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------- | ||||
| # Protocol steps | ||||
| # -------------------------- | ||||
| 
 | ||||
| def run_prefix_step(S, K, clause_limit): | ||||
|     clauses = [] | ||||
| 
 | ||||
|     def on_clause(premises, deps, clause, status): | ||||
|         print(f"  [OnClause] collected clause status: {status}, clause: {clause}") | ||||
|         if len(clauses) < clause_limit: | ||||
|             clauses.append(clause) | ||||
| 
 | ||||
|     OnClause(S, on_clause) | ||||
|     S.set("max_conflicts", K) | ||||
|     r = S.check() | ||||
|     return r, clauses | ||||
| 
 | ||||
| # Replay proof prefix on an existing PPS_solver (no solver recreation) | ||||
| # Solver continues from its current state. | ||||
| def replay_prefix_on_pps(PPS_solver, clauses, param_state, budget): | ||||
|     print(f"[Replaying] on PPS with params={param_state} and budget={budget}") | ||||
|     apply_param_state(PPS_solver, param_state) | ||||
| 
 | ||||
|     total_conflicts = total_decisions = total_rlimit = 0 | ||||
| 
 | ||||
|     # For each learned clause Cj = [l1, l2, ...], check ¬(l1 ∨ l2 ∨ ...) | ||||
|     for idx, Cj in enumerate(clauses): | ||||
|         lits = [l.translate(PPS_solver.ctx) for l in Cj] | ||||
| 
 | ||||
|         negated_lits = [] | ||||
|         for l in lits: | ||||
|             negated_lits.append(Not(l)) | ||||
| 
 | ||||
|         PPS_solver.set("max_conflicts", budget) | ||||
|         r = PPS_solver.check(negated_lits) | ||||
|         st = PPS_solver.statistics() | ||||
| 
 | ||||
|         c, d, rl = stats_tuple(st) | ||||
|         total_conflicts += c | ||||
|         total_decisions += d | ||||
|         total_rlimit += rl | ||||
| 
 | ||||
|         print(f"  [C{idx}] result={r}, conflicts={c}, decisions={d}, rlimit={rl}") | ||||
| 
 | ||||
|     return (total_conflicts, total_decisions, total_rlimit) | ||||
| 
 | ||||
| 
 | ||||
| # For each PPS_i, replay the proof prefix of S | ||||
| def replay_proof_prefixes(clauses, param_states, PPS_solvers, K, eps=200): | ||||
|     budget = K + eps | ||||
|     base_param_state, candidate_param_states = param_states[0], param_states[1:] | ||||
| 
 | ||||
|     # PPS_0 (baseline) | ||||
|     score0 = replay_prefix_on_pps(PPS_solvers[0], clauses, base_param_state, budget) | ||||
|     best_param_state, best_score = base_param_state, score0 | ||||
|      | ||||
|     # PPS_i, i > 0 | ||||
|     for i, p_state in enumerate(candidate_param_states, start=1): | ||||
|         score = replay_prefix_on_pps(PPS_solvers[i], clauses, p_state, budget) | ||||
|         if score < best_score: | ||||
|             best_param_state, best_score = p_state, score | ||||
| 
 | ||||
|     return best_param_state, best_score | ||||
| 
 | ||||
| # return a variant of the given param state | ||||
| def perturbate(param_state): | ||||
|     new_state = [] | ||||
|     for name, val in param_state: | ||||
|         if isinstance(val, (int, float)) and "restart_factor" in name: | ||||
|             # perturb multiplicatively +/-10% | ||||
|             factor = random.choice([0.9, 1.1]) | ||||
|             new_state.append((name, round(val * factor, 3))) | ||||
|         elif isinstance(val, int) and "phase_caching" in name: | ||||
|             # pick half or double | ||||
|             new_val = random.choice([max(1, val // 2), val * 2]) | ||||
|             new_state.append((name, new_val)) | ||||
|         elif name == "smt.relevancy": | ||||
|             # pick random alternative from {0,1,2} | ||||
|             new_val = random.choice([0, 1, 2]) | ||||
|             new_state.append((name, new_val)) | ||||
|         else: | ||||
|             # unchanged | ||||
|             new_state.append((name, val)) | ||||
|     return new_state | ||||
| 
 | ||||
| # -------------------------- | ||||
| # Protocol iteration | ||||
| # -------------------------- | ||||
| 
 | ||||
| def protocol_iteration(filepath, manager, S, PPS_solvers, PPS_states, K, eps=200): | ||||
|     # --- Proof Prefix Solver (S) --- | ||||
|     P = manager.best_param_state or BASE_PARAM_CANDIDATES | ||||
|     apply_param_state(S, P) | ||||
| 
 | ||||
|     # Run S with max conflicts K | ||||
|     # Simultaneously, collect subset of conflict clauses from the bounded run of S.  | ||||
|     # Right now clause collection is pretty naive as we just take the first clause_limit clauses from OnClause | ||||
|     print(f"[S] Running proof prefix solver with params={P} and max_conflicts={K}") | ||||
|     r, C_list = run_prefix_step(S, K, clause_limit=MAX_EXAMPLES) | ||||
| 
 | ||||
|     # If S returns SAT or UNSAT we have a verdict | ||||
|     # Tell the central dispatch that search is complete and exit | ||||
|     if r == sat or r == unsat: | ||||
|         print(f"[S] {os.path.basename(filepath)} → {r} (within max_conflicts={K}). Search complete.") | ||||
|         manager.mark_complete() | ||||
|         return | ||||
| 
 | ||||
|     # For each PPS_i, replay the proof prefix of S | ||||
|     print(f"[Replaying] Replaying proof prefix on PPS solvers with budget={K + eps}") | ||||
|     best_state, best_score = replay_proof_prefixes(C_list, PPS_states, PPS_solvers, K, eps) | ||||
| 
 | ||||
|     if best_state != P: | ||||
|         print(f"[Dispatch] updating best param state") | ||||
|         manager.maybe_update_best(best_state, best_score) | ||||
|         P = best_state | ||||
| 
 | ||||
|     # Update PPS_0 to use P (if it changed), and update all PPS_i > 0 with new perturbations of P | ||||
|     PPS_states[0] = P  | ||||
|     for i in range(1, len(PPS_states)): | ||||
|         PPS_states[i] = perturbate(P) | ||||
|          | ||||
|     return PPS_states | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------- | ||||
| # Prefix probing thread | ||||
| # -------------------------- | ||||
| 
 | ||||
| def prefix_probe_thread(filepath, manager): | ||||
|     # Proof prefix solver S | ||||
|     S = solver_from_file(filepath) | ||||
|     apply_param_state(S, BASE_PARAM_CANDIDATES) | ||||
| 
 | ||||
|     PPS_solvers = [] | ||||
|     PPS_states = [] | ||||
| 
 | ||||
|     # set up the 4 variant parameter probe solvers PPS_1 ... PPS_4 as new contexts on the proof prefix solver S | ||||
|     for i in range(4): | ||||
|         st = BASE_PARAM_CANDIDATES if i == 0 else perturbate(BASE_PARAM_CANDIDATES) # PPS_0 uses base params | ||||
|         ctx = Context() | ||||
|         PPS_solver = S.translate(ctx)  # clone S (proof prefix) into new context | ||||
|         apply_param_state(PPS_solver, st) | ||||
|         PPS_solvers.append(PPS_solver) | ||||
|         PPS_states.append(st) | ||||
|         print(f"[Init] PPS_{i} inherited prefix in new context with params={st}") | ||||
| 
 | ||||
|     # Reuse the same solvers each iteration | ||||
|     iteration = 0 | ||||
|     while not manager.search_complete: | ||||
|         print(f"\n[PrefixThread] Iteration {iteration}") | ||||
|         PPS_states = protocol_iteration(filepath, manager, S, PPS_solvers, PPS_states, K=MAX_CONFLICTS, eps=200) | ||||
|         iteration += 1 | ||||
| 
 | ||||
| 
 | ||||
| # -------------------------- | ||||
| # Main | ||||
| # -------------------------- | ||||
| 
 | ||||
| def run_main_solver(filepath): | ||||
|     set_param("parallel.enable", True) | ||||
|     main_solver = solver_from_file(filepath) | ||||
|     apply_param_state(main_solver, BASE_PARAM_CANDIDATES) | ||||
|     print(f"[Main] Started main solver on {os.path.basename(filepath)} with parallel.enable=True") | ||||
|     r = main_solver.check() | ||||
|     print(f"[Main] {os.path.basename(filepath)} → {r}") | ||||
| 
 | ||||
| def main(): | ||||
|     manager = BatchManager() | ||||
|     for benchmark in os.listdir(bench_dir): | ||||
|         if benchmark != "From_T2__hqr.t2_fixed__term_unfeasibility_1_0.smt2": | ||||
|             continue | ||||
| 
 | ||||
|         filepath = os.path.join(bench_dir, benchmark) | ||||
|         prefix_proc = Process(target=prefix_probe_thread, args=(filepath, manager)) | ||||
|         main_proc = Process(target=run_main_solver, args=(filepath,)) | ||||
|         prefix_proc.start() | ||||
|         main_proc.start() | ||||
|         prefix_proc.join() | ||||
|         main_proc.join() | ||||
| 
 | ||||
|     if manager.best_param_state: | ||||
|         print(f"\n[GLOBAL] Best parameter state: {manager.best_param_state} with score {manager.best_score}") | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main() | ||||
|  | @ -1941,7 +1941,7 @@ _error_handler_type  = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_uint) | |||
| _lib.Z3_set_error_handler.restype  = None | ||||
| _lib.Z3_set_error_handler.argtypes = [ContextObj, _error_handler_type] | ||||
| 
 | ||||
| Z3_on_clause_eh = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), ctypes.c_void_p) | ||||
| Z3_on_clause_eh = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint), ctypes.c_void_p, ctypes.c_uint) | ||||
| Z3_push_eh  = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p) | ||||
| Z3_pop_eh   = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint) | ||||
| Z3_fresh_eh = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) | ||||
|  |  | |||
|  | @ -306,12 +306,24 @@ extern "C" { | |||
|         Z3_CATCH; | ||||
|     } | ||||
| 
 | ||||
|     static datatype_decl* mk_datatype_decl(Z3_context c, | ||||
|                                            Z3_symbol name, | ||||
|                                            unsigned num_constructors, | ||||
|                                            Z3_constructor constructors[]) { | ||||
|     static datatype_decl* api_datatype_decl(Z3_context c, | ||||
|                                             Z3_symbol name, | ||||
|                                             unsigned num_parameters, | ||||
|                                             Z3_sort const parameters[], | ||||
|                                             unsigned num_constructors, | ||||
|                                             Z3_constructor constructors[]) { | ||||
|         datatype_util& dt_util = mk_c(c)->dtutil(); | ||||
|         ast_manager& m = mk_c(c)->m(); | ||||
|          | ||||
|         sort_ref_vector params(m); | ||||
|          | ||||
|         // A correct use of the API is to always provide parameters explicitly.
 | ||||
|         // implicit parameters through polymorphic type variables does not work
 | ||||
|         // because the order of polymorphic variables in the parameters is ambiguous.
 | ||||
|         if (num_parameters > 0 && parameters)  | ||||
|             for (unsigned i = 0; i < num_parameters; ++i)  | ||||
|                 params.push_back(to_sort(parameters[i])); | ||||
|          | ||||
|         ptr_vector<constructor_decl> constrs; | ||||
|         for (unsigned i = 0; i < num_constructors; ++i) { | ||||
|             constructor* cn = reinterpret_cast<constructor*>(constructors[i]); | ||||
|  | @ -326,7 +338,7 @@ extern "C" { | |||
|             } | ||||
|             constrs.push_back(mk_constructor_decl(cn->m_name, cn->m_tester, acc.size(), acc.data())); | ||||
|         } | ||||
|         return mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, num_constructors, constrs.data()); | ||||
|         return mk_datatype_decl(dt_util, to_symbol(name), params.size(), params.data(), num_constructors, constrs.data()); | ||||
|     } | ||||
| 
 | ||||
|     Z3_sort Z3_API Z3_mk_datatype(Z3_context c, | ||||
|  | @ -341,7 +353,7 @@ extern "C" { | |||
| 
 | ||||
|         sort_ref_vector sorts(m); | ||||
|         { | ||||
|             datatype_decl * data = mk_datatype_decl(c, name, num_constructors, constructors); | ||||
|             datatype_decl * data = api_datatype_decl(c, name, 0, nullptr, num_constructors, constructors); | ||||
|             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &data, 0, nullptr, sorts); | ||||
|             del_datatype_decl(data); | ||||
| 
 | ||||
|  | @ -363,6 +375,42 @@ extern "C" { | |||
|         Z3_CATCH_RETURN(nullptr); | ||||
|     } | ||||
| 
 | ||||
|     Z3_sort Z3_API Z3_mk_polymorphic_datatype(Z3_context c, | ||||
|                                               Z3_symbol name, | ||||
|                                               unsigned num_parameters, | ||||
|                                               Z3_sort parameters[], | ||||
|                                               unsigned num_constructors, | ||||
|                                               Z3_constructor constructors[]) { | ||||
|         Z3_TRY; | ||||
|         LOG_Z3_mk_polymorphic_datatype(c, name, num_parameters, parameters, num_constructors, constructors); | ||||
|         RESET_ERROR_CODE(); | ||||
|         ast_manager& m = mk_c(c)->m(); | ||||
|         datatype_util data_util(m); | ||||
| 
 | ||||
|         sort_ref_vector sorts(m); | ||||
|         { | ||||
|             datatype_decl * data = api_datatype_decl(c, name, num_parameters, parameters, num_constructors, constructors); | ||||
|             bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &data, 0, nullptr, sorts); | ||||
|             del_datatype_decl(data); | ||||
| 
 | ||||
|             if (!is_ok) { | ||||
|                 SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); | ||||
|                 RETURN_Z3(nullptr); | ||||
|             } | ||||
|         } | ||||
|         sort * s = sorts.get(0); | ||||
| 
 | ||||
|         mk_c(c)->save_ast_trail(s); | ||||
|         ptr_vector<func_decl> const& cnstrs = *data_util.get_datatype_constructors(s); | ||||
| 
 | ||||
|         for (unsigned i = 0; i < num_constructors; ++i) { | ||||
|             constructor* cn = reinterpret_cast<constructor*>(constructors[i]); | ||||
|             cn->m_constructor = cnstrs[i]; | ||||
|         } | ||||
|         RETURN_Z3_mk_polymorphic_datatype(of_sort(s)); | ||||
|         Z3_CATCH_RETURN(nullptr); | ||||
|     } | ||||
| 
 | ||||
|     typedef ptr_vector<constructor> constructor_list; | ||||
| 
 | ||||
|     Z3_constructor_list Z3_API Z3_mk_constructor_list(Z3_context c, | ||||
|  | @ -387,14 +435,18 @@ extern "C" { | |||
|         Z3_CATCH; | ||||
|     } | ||||
| 
 | ||||
|     Z3_sort Z3_API Z3_mk_datatype_sort(Z3_context c, Z3_symbol name) { | ||||
|     Z3_sort Z3_API Z3_mk_datatype_sort(Z3_context c, Z3_symbol name, unsigned num_params, Z3_sort const params[]) { | ||||
|         Z3_TRY; | ||||
|         LOG_Z3_mk_datatype_sort(c, name); | ||||
|         LOG_Z3_mk_datatype_sort(c, name, num_params, params); | ||||
|         RESET_ERROR_CODE(); | ||||
|         ast_manager& m = mk_c(c)->m(); | ||||
|         datatype_util adt_util(m); | ||||
|         parameter p(to_symbol(name)); | ||||
|         sort * s = m.mk_sort(adt_util.get_family_id(), DATATYPE_SORT, 1, &p); | ||||
|         vector<parameter> ps; | ||||
|         ps.push_back(parameter(to_symbol(name))); | ||||
|         for (unsigned i = 0; i < num_params; ++i) { | ||||
|             ps.push_back(parameter(to_sort(params[i]))); | ||||
|         } | ||||
|         sort * s = m.mk_sort(adt_util.get_family_id(), DATATYPE_SORT, ps.size(), ps.data()); | ||||
|         mk_c(c)->save_ast_trail(s); | ||||
|         RETURN_Z3(of_sort(s)); | ||||
|         Z3_CATCH_RETURN(nullptr); | ||||
|  | @ -416,7 +468,7 @@ extern "C" { | |||
|         ptr_vector<datatype_decl> datas; | ||||
|         for (unsigned i = 0; i < num_sorts; ++i) { | ||||
|             constructor_list* cl = reinterpret_cast<constructor_list*>(constructor_lists[i]); | ||||
|             datas.push_back(mk_datatype_decl(c, sort_names[i], cl->size(), reinterpret_cast<Z3_constructor*>(cl->data()))); | ||||
|             datas.push_back(api_datatype_decl(c, sort_names[i], 0, nullptr, cl->size(), reinterpret_cast<Z3_constructor*>(cl->data()))); | ||||
|         } | ||||
|         sort_ref_vector _sorts(m); | ||||
|         bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.data(), 0, nullptr, _sorts); | ||||
|  |  | |||
|  | @ -1031,14 +1031,14 @@ extern "C" { | |||
|         Z3_TRY; | ||||
|         RESET_ERROR_CODE(); | ||||
|         init_solver(c, s);      | ||||
|         user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned nd, unsigned const* deps, unsigned n, expr* const* _literals) { | ||||
|         user_propagator::on_clause_eh_t _on_clause = [=](void* user_ctx, expr* proof, unsigned nd, unsigned const* deps, unsigned n, expr* const* _literals, unsigned const status) { | ||||
|             Z3_ast_vector_ref * literals = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); | ||||
|             mk_c(c)->save_object(literals); | ||||
|             expr_ref pr(proof, mk_c(c)->m()); | ||||
|             scoped_ast_vector _sc(literals); | ||||
|             for (unsigned i = 0; i < n; ++i) | ||||
|                 literals->m_ast_vector.push_back(_literals[i]); | ||||
|             on_clause_eh(user_ctx, of_expr(pr.get()), nd, deps, of_ast_vector(literals)); | ||||
|             on_clause_eh(user_ctx, of_expr(pr.get()), nd, deps, of_ast_vector(literals), status); | ||||
|         }; | ||||
|         to_solver_ref(s)->register_on_clause(user_context, _on_clause); | ||||
|         auto& solver = *to_solver(s); | ||||
|  |  | |||
|  | @ -343,6 +343,14 @@ namespace z3 { | |||
|         */ | ||||
|         sort datatype_sort(symbol const& name); | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief a reference to a recursively defined parametric datatype. | ||||
|            Expect that it gets defined as a \ref datatype. | ||||
|            \param name name of the datatype | ||||
|            \param params sort parameters | ||||
|         */ | ||||
|         sort datatype_sort(symbol const& name, sort_vector const& params); | ||||
| 
 | ||||
|              | ||||
|         /**
 | ||||
|            \brief create an uninterpreted sort with the name given by the string or symbol. | ||||
|  | @ -3625,7 +3633,14 @@ namespace z3 { | |||
| 
 | ||||
| 
 | ||||
|     inline sort context::datatype_sort(symbol const& name) { | ||||
|         Z3_sort s = Z3_mk_datatype_sort(*this, name); | ||||
|         Z3_sort s = Z3_mk_datatype_sort(*this, name, 0, nullptr); | ||||
|         check_error(); | ||||
|         return sort(*this, s);             | ||||
|     } | ||||
| 
 | ||||
|     inline sort context::datatype_sort(symbol const& name, sort_vector const& params) { | ||||
|         array<Z3_sort> _params(params); | ||||
|         Z3_sort s = Z3_mk_datatype_sort(*this, name, _params.size(), _params.ptr()); | ||||
|         check_error(); | ||||
|         return sort(*this, s);             | ||||
|     } | ||||
|  | @ -4273,20 +4288,20 @@ namespace z3 { | |||
|         return expr(ctx(), r); | ||||
|     } | ||||
| 
 | ||||
|     typedef std::function<void(expr const& proof, std::vector<unsigned> const& deps, expr_vector const& clause)> on_clause_eh_t; | ||||
|     typedef std::function<void(expr const& proof, std::vector<unsigned> const& deps, expr_vector const& clause, unsigned const status)> on_clause_eh_t; | ||||
| 
 | ||||
|     class on_clause { | ||||
|         context& c; | ||||
|         on_clause_eh_t m_on_clause; | ||||
| 
 | ||||
|         static void _on_clause_eh(void* _ctx, Z3_ast _proof, unsigned n, unsigned const* dep, Z3_ast_vector _literals) { | ||||
|         static void _on_clause_eh(void* _ctx, Z3_ast _proof, unsigned n, unsigned const* dep, Z3_ast_vector _literals, unsigned const status) { | ||||
|             on_clause* ctx = static_cast<on_clause*>(_ctx); | ||||
|             expr_vector lits(ctx->c, _literals); | ||||
|             expr proof(ctx->c, _proof); | ||||
|             std::vector<unsigned> deps; | ||||
|             for (unsigned i = 0; i < n; ++i) | ||||
|                 deps.push_back(dep[i]); | ||||
|             ctx->m_on_clause(proof, deps, lits); | ||||
|             ctx->m_on_clause(proof, deps, lits, status); | ||||
|         } | ||||
|     public: | ||||
|         on_clause(solver& s, on_clause_eh_t& on_clause_eh): c(s.ctx()) { | ||||
|  |  | |||
|  | @ -474,6 +474,36 @@ namespace Microsoft.Z3 | |||
|             return new DatatypeSort(this, symbol, constructors); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Create a forward reference to a datatype sort. | ||||
|         /// This is useful for creating recursive datatypes or parametric datatypes. | ||||
|         /// </summary> | ||||
|         /// <param name="name">name of the datatype sort</param> | ||||
|         /// <param name="parameters">optional array of sort parameters for parametric datatypes</param> | ||||
|         public DatatypeSort MkDatatypeSortRef(Symbol name, Sort[] parameters = null) | ||||
|         { | ||||
|             Debug.Assert(name != null); | ||||
|             CheckContextMatch(name); | ||||
|             if (parameters != null) | ||||
|                 CheckContextMatch<Sort>(parameters); | ||||
|              | ||||
|             var numParams = (parameters == null) ? 0 : (uint)parameters.Length; | ||||
|             var paramsNative = (parameters == null) ? null : AST.ArrayToNative(parameters); | ||||
|             return new DatatypeSort(this, Native.Z3_mk_datatype_sort(nCtx, name.NativeObject, numParams, paramsNative)); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Create a forward reference to a datatype sort. | ||||
|         /// This is useful for creating recursive datatypes or parametric datatypes. | ||||
|         /// </summary> | ||||
|         /// <param name="name">name of the datatype sort</param> | ||||
|         /// <param name="parameters">optional array of sort parameters for parametric datatypes</param> | ||||
|         public DatatypeSort MkDatatypeSortRef(string name, Sort[] parameters = null) | ||||
|         { | ||||
|             using var symbol = MkSymbol(name); | ||||
|             return MkDatatypeSortRef(symbol, parameters); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Create mutually recursive datatypes. | ||||
|         /// </summary> | ||||
|  |  | |||
|  | @ -388,6 +388,54 @@ public class Context implements AutoCloseable { | |||
|         return new DatatypeSort<>(this, mkSymbol(name), constructors); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a forward reference to a datatype sort. | ||||
|      * This is useful for creating recursive datatypes or parametric datatypes. | ||||
|      * @param name name of the datatype sort | ||||
|      * @param params optional array of sort parameters for parametric datatypes | ||||
|      **/ | ||||
|     public <R> DatatypeSort<R> mkDatatypeSortRef(Symbol name, Sort[] params) | ||||
|     { | ||||
|         checkContextMatch(name); | ||||
|         if (params != null) | ||||
|             checkContextMatch(params); | ||||
|          | ||||
|         int numParams = (params == null) ? 0 : params.length; | ||||
|         long[] paramsNative = (params == null) ? new long[0] : AST.arrayToNative(params); | ||||
|         return new DatatypeSort<>(this, Native.mkDatatypeSort(nCtx(), name.getNativeObject(), numParams, paramsNative)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a forward reference to a datatype sort (non-parametric). | ||||
|      * This is useful for creating recursive datatypes. | ||||
|      * @param name name of the datatype sort | ||||
|      **/ | ||||
|     public <R> DatatypeSort<R> mkDatatypeSortRef(Symbol name) | ||||
|     { | ||||
|         return mkDatatypeSortRef(name, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a forward reference to a datatype sort. | ||||
|      * This is useful for creating recursive datatypes or parametric datatypes. | ||||
|      * @param name name of the datatype sort | ||||
|      * @param params optional array of sort parameters for parametric datatypes | ||||
|      **/ | ||||
|     public <R> DatatypeSort<R> mkDatatypeSortRef(String name, Sort[] params) | ||||
|     { | ||||
|         return mkDatatypeSortRef(mkSymbol(name), params); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create a forward reference to a datatype sort (non-parametric). | ||||
|      * This is useful for creating recursive datatypes. | ||||
|      * @param name name of the datatype sort | ||||
|      **/ | ||||
|     public <R> DatatypeSort<R> mkDatatypeSortRef(String name) | ||||
|     { | ||||
|         return mkDatatypeSortRef(name, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create mutually recursive datatypes.  | ||||
|      * @param names names of datatype sorts  | ||||
|  |  | |||
|  | @ -909,11 +909,17 @@ struct | |||
|     mk_sort ctx (Symbol.mk_string ctx name) constructors | ||||
| 
 | ||||
|   let mk_sort_ref (ctx: context) (name:Symbol.symbol) = | ||||
|     Z3native.mk_datatype_sort ctx name | ||||
|     Z3native.mk_datatype_sort ctx name 0 [] | ||||
| 
 | ||||
|   let mk_sort_ref_s (ctx: context) (name: string) = | ||||
|     mk_sort_ref ctx (Symbol.mk_string ctx name) | ||||
| 
 | ||||
|   let mk_sort_ref_p (ctx: context) (name:Symbol.symbol) (params:Sort.sort list) = | ||||
|     Z3native.mk_datatype_sort ctx name (List.length params) params | ||||
| 
 | ||||
|   let mk_sort_ref_ps (ctx: context) (name: string) (params:Sort.sort list) = | ||||
|     mk_sort_ref_p ctx (Symbol.mk_string ctx name) params | ||||
| 
 | ||||
|   let mk_sorts (ctx:context) (names:Symbol.symbol list) (c:Constructor.constructor list list) = | ||||
|     let n = List.length names in | ||||
|     let f e = ConstructorList.create ctx e in | ||||
|  |  | |||
|  | @ -1087,6 +1087,12 @@ sig | |||
|   (* [mk_sort_ref_s ctx s] is [mk_sort_ref ctx (Symbol.mk_string ctx s)] *) | ||||
|   val mk_sort_ref_s : context -> string -> Sort.sort | ||||
| 
 | ||||
|   (** Create a forward reference to a parametric datatype sort. *) | ||||
|   val mk_sort_ref_p : context -> Symbol.symbol -> Sort.sort list -> Sort.sort | ||||
| 
 | ||||
|   (** Create a forward reference to a parametric datatype sort. *) | ||||
|   val mk_sort_ref_ps : context -> string -> Sort.sort list -> Sort.sort | ||||
| 
 | ||||
|   (** Create a new datatype sort. *) | ||||
|   val mk_sort : context -> Symbol.symbol -> Constructor.constructor list -> Sort.sort | ||||
| 
 | ||||
|  |  | |||
|  | @ -1245,6 +1245,18 @@ def _coerce_expr_merge(s, a): | |||
|     else: | ||||
|         return s | ||||
| 
 | ||||
| def _check_same_sort(a, b, ctx=None): | ||||
|     if not isinstance(a, ExprRef): | ||||
|         return False | ||||
|     if not isinstance(b, ExprRef): | ||||
|         return False | ||||
|     if ctx is None: | ||||
|         ctx = a.ctx | ||||
| 
 | ||||
|     a_sort = Z3_get_sort(ctx.ctx, a.ast) | ||||
|     b_sort = Z3_get_sort(ctx.ctx, b.ast) | ||||
|     return Z3_is_eq_sort(ctx.ctx, a_sort, b_sort) | ||||
| 
 | ||||
| 
 | ||||
| def _coerce_exprs(a, b, ctx=None): | ||||
|     if not is_expr(a) and not is_expr(b): | ||||
|  | @ -1259,6 +1271,9 @@ def _coerce_exprs(a, b, ctx=None): | |||
|     if isinstance(b, float) and isinstance(a, ArithRef): | ||||
|         b = RealVal(b, a.ctx) | ||||
| 
 | ||||
|     if _check_same_sort(a, b, ctx): | ||||
|         return (a, b) | ||||
| 
 | ||||
|     s = None | ||||
|     s = _coerce_expr_merge(s, a) | ||||
|     s = _coerce_expr_merge(s, b) | ||||
|  | @ -5474,10 +5489,30 @@ class DatatypeRef(ExprRef): | |||
|         """Return the datatype sort of the datatype expression `self`.""" | ||||
|         return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx) | ||||
| 
 | ||||
| def DatatypeSort(name, ctx = None): | ||||
|     """Create a reference to a sort that was declared, or will be declared, as a recursive datatype""" | ||||
| def DatatypeSort(name, params=None, ctx=None): | ||||
|     """Create a reference to a sort that was declared, or will be declared, as a recursive datatype. | ||||
|      | ||||
|     Args: | ||||
|         name: name of the datatype sort | ||||
|         params: optional list/tuple of sort parameters for parametric datatypes | ||||
|         ctx: Z3 context (optional) | ||||
|      | ||||
|     Example: | ||||
|         >>> # Non-parametric datatype | ||||
|         >>> TreeRef = DatatypeSort('Tree') | ||||
|         >>> # Parametric datatype with one parameter | ||||
|         >>> ListIntRef = DatatypeSort('List', [IntSort()]) | ||||
|         >>> # Parametric datatype with multiple parameters | ||||
|         >>> PairRef = DatatypeSort('Pair', [IntSort(), BoolSort()]) | ||||
|     """ | ||||
|     ctx = _get_ctx(ctx) | ||||
|     return DatatypeSortRef(Z3_mk_datatype_sort(ctx.ref(), to_symbol(name, ctx)), ctx) | ||||
|     if params is None or len(params) == 0: | ||||
|         return DatatypeSortRef(Z3_mk_datatype_sort(ctx.ref(), to_symbol(name, ctx), 0, (Sort * 0)()), ctx) | ||||
|     else: | ||||
|         _params = (Sort * len(params))() | ||||
|         for i in range(len(params)): | ||||
|             _params[i] = params[i].ast | ||||
|         return DatatypeSortRef(Z3_mk_datatype_sort(ctx.ref(), to_symbol(name, ctx), len(params), _params), ctx) | ||||
| 
 | ||||
| def TupleSort(name, sorts, ctx=None): | ||||
|     """Create a named tuple sort base on a set of underlying sorts | ||||
|  | @ -11677,12 +11712,12 @@ def to_AstVectorObj(ptr,): | |||
| # for UserPropagator we use a global dictionary, which isn't great code. | ||||
| 
 | ||||
| _my_hacky_class = None | ||||
| def on_clause_eh(ctx, p, n, dep, clause): | ||||
| def on_clause_eh(ctx, p, n, dep, clause, status): | ||||
|     onc = _my_hacky_class | ||||
|     p = _to_expr_ref(to_Ast(p), onc.ctx) | ||||
|     clause = AstVector(to_AstVectorObj(clause), onc.ctx) | ||||
|     deps = [dep[i] for i in range(n)] | ||||
|     onc.on_clause(p, deps, clause) | ||||
|     onc.on_clause(p, deps, clause, status) | ||||
|      | ||||
| _on_clause_eh = Z3_on_clause_eh(on_clause_eh) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1441,7 +1441,7 @@ Z3_DECLARE_CLOSURE(Z3_final_eh,   void, (void* ctx, Z3_solver_callback cb)); | |||
| Z3_DECLARE_CLOSURE(Z3_created_eh, void, (void* ctx, Z3_solver_callback cb, Z3_ast t)); | ||||
| Z3_DECLARE_CLOSURE(Z3_decide_eh,  void, (void* ctx, Z3_solver_callback cb, Z3_ast t, unsigned idx, bool phase)); | ||||
| Z3_DECLARE_CLOSURE(Z3_on_binding_eh, bool, (void* ctx, Z3_solver_callback cb, Z3_ast q, Z3_ast inst)); | ||||
| Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, unsigned n, unsigned const* deps, Z3_ast_vector literals)); | ||||
| Z3_DECLARE_CLOSURE(Z3_on_clause_eh, void, (void* ctx, Z3_ast proof_hint, unsigned n, unsigned const* deps, Z3_ast_vector literals, unsigned const status)); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -2127,6 +2127,33 @@ extern "C" { | |||
|                                   unsigned num_constructors, | ||||
|                                   Z3_constructor constructors[]); | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Create a parametric datatype with explicit type parameters. | ||||
|         | ||||
|        This function is similar to #Z3_mk_datatype, except it takes an explicit set of type parameters. | ||||
|        The parameters can be type variables created with #Z3_mk_type_variable, allowing the definition | ||||
|        of polymorphic datatypes that can be instantiated with different concrete types. | ||||
| 
 | ||||
|        \param c logical context | ||||
|        \param name name of the datatype | ||||
|        \param num_parameters number of type parameters (can be 0) | ||||
|        \param parameters array of type parameters (type variables or concrete sorts) | ||||
|        \param num_constructors number of constructors | ||||
|        \param constructors array of constructor specifications | ||||
| 
 | ||||
|        \sa Z3_mk_datatype | ||||
|        \sa Z3_mk_type_variable | ||||
|        \sa Z3_mk_datatype_sort | ||||
| 
 | ||||
|        def_API('Z3_mk_polymorphic_datatype', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(UINT), _inout_array(4, CONSTRUCTOR))) | ||||
|      */ | ||||
|     Z3_sort Z3_API Z3_mk_polymorphic_datatype(Z3_context c, | ||||
|                                               Z3_symbol name, | ||||
|                                               unsigned num_parameters, | ||||
|                                               Z3_sort parameters[], | ||||
|                                               unsigned num_constructors, | ||||
|                                               Z3_constructor constructors[]); | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief create a forward reference to a recursive datatype being declared. | ||||
|        The forward reference can be used in a nested occurrence: the range of an array | ||||
|  | @ -2136,9 +2163,14 @@ extern "C" { | |||
|        Forward references can replace the use sort references, that are unsigned integers | ||||
|        in the \c Z3_mk_constructor call | ||||
| 
 | ||||
|        def_API('Z3_mk_datatype_sort', SORT, (_in(CONTEXT), _in(SYMBOL))) | ||||
|        \param c logical context | ||||
|        \param name name of the datatype | ||||
|        \param num_params number of sort parameters   | ||||
|        \param params array of sort parameters | ||||
| 
 | ||||
|        def_API('Z3_mk_datatype_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT))) | ||||
|      */ | ||||
|     Z3_sort Z3_API Z3_mk_datatype_sort(Z3_context c, Z3_symbol name); | ||||
|     Z3_sort Z3_API Z3_mk_datatype_sort(Z3_context c, Z3_symbol name, unsigned num_params, Z3_sort const params[]); | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Create list of constructors. | ||||
|  |  | |||
|  | @ -300,6 +300,12 @@ namespace datatype { | |||
|                         TRACE(datatype, tout << "expected sort parameter at position " << i << " got: " << s << "\n";); | ||||
|                         throw invalid_datatype(); | ||||
|                     } | ||||
|                     // Allow type variables as parameters for polymorphic datatypes
 | ||||
|                     sort* param_sort = to_sort(s.get_ast()); | ||||
|                     if (!m_manager->is_type_var(param_sort) && param_sort->get_family_id() == null_family_id) { | ||||
|                         // Type variables and concrete sorts are allowed, but not other uninterpreted sorts
 | ||||
|                         // Actually, all sorts should be allowed including uninterpreted ones
 | ||||
|                     } | ||||
|                 } | ||||
|                                  | ||||
|                 sort* s = m_manager->mk_sort(name.get_symbol(), | ||||
|  |  | |||
|  | @ -566,7 +566,8 @@ struct nnf::imp { | |||
|         expr * _then      = rs[2]; | ||||
|         expr * _else      = rs[3]; | ||||
| 
 | ||||
|         app * r = m.mk_and(m.mk_or(_not_cond, _then), m.mk_or(_cond, _else)); | ||||
|         expr* a = m.mk_or(_not_cond, _then); | ||||
|         app * r = m.mk_and(a, m.mk_or(_cond, _else)); | ||||
|         m_result_stack.shrink(fr.m_spos); | ||||
|         m_result_stack.push_back(r); | ||||
|         if (proofs_enabled()) { | ||||
|  | @ -612,11 +613,13 @@ struct nnf::imp { | |||
| 
 | ||||
|         app * r; | ||||
|         if (is_eq(t) == fr.m_pol) { | ||||
|             auto a = m.mk_or(not_lhs, rhs); | ||||
|             expr* a = m.mk_or(not_lhs, rhs); | ||||
|             r = m.mk_and(a, m.mk_or(lhs, not_rhs)); | ||||
|         } | ||||
|         else | ||||
|             r = m.mk_and(m.mk_or(lhs, rhs), m.mk_or(not_lhs, not_rhs)); | ||||
|         else { | ||||
|             expr* a = m.mk_or(lhs, rhs); | ||||
|             r = m.mk_and(a, m.mk_or(not_lhs, not_rhs)); | ||||
|         } | ||||
|         m_result_stack.shrink(fr.m_spos); | ||||
|         m_result_stack.push_back(r); | ||||
|         if (proofs_enabled()) { | ||||
|  | @ -688,8 +691,8 @@ struct nnf::imp { | |||
|             if (proofs_enabled()) { | ||||
|                 expr_ref aux(m); | ||||
|                 aux = m.mk_label(true, names.size(), names.data(), arg); | ||||
|                 pr = m.mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), | ||||
|                                          m.mk_iff_oeq(m.mk_rewrite(aux, r))); | ||||
|                 auto a = mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)); | ||||
|                 pr = m.mk_transitivity(a, m.mk_iff_oeq(m.mk_rewrite(aux, r))); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|  |  | |||
|  | @ -720,24 +720,36 @@ br_status arith_rewriter::mk_le_ge_eq_core(expr * arg1, expr * arg2, op_kind kin | |||
|     } | ||||
|     expr* c = nullptr, *t = nullptr, *e = nullptr; | ||||
|     if (m.is_ite(arg1, c, t, e) && is_numeral(t, a1) && is_numeral(arg2, a2)) { | ||||
|         expr_ref a(m.mk_not(c), m); | ||||
|         switch (kind) { | ||||
|         case LE: result = a1 <= a2 ? m.mk_or(c, m_util.mk_le(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_le(e, arg2)); return BR_REWRITE2; | ||||
|         case GE: result = a1 >= a2 ? m.mk_or(c, m_util.mk_ge(e, arg2)) : m.mk_and(m.mk_not(c), m_util.mk_ge(e, arg2)); return BR_REWRITE2; | ||||
|         case EQ: result = a1 == a2 ? m.mk_or(c, m.mk_eq(e, arg2))    : m.mk_and(m.mk_not(c), m_util.mk_eq(e, arg2)); return BR_REWRITE2; | ||||
|         case LE: result = a1 <= a2 ? m.mk_or(c, m_util.mk_le(e, arg2)) : m.mk_and(a, m_util.mk_le(e, arg2)); return BR_REWRITE2; | ||||
|         case GE: result = a1 >= a2 ? m.mk_or(c, m_util.mk_ge(e, arg2)) : m.mk_and(a, m_util.mk_ge(e, arg2)); return BR_REWRITE2; | ||||
|         case EQ: result = a1 == a2 ? m.mk_or(c, m.mk_eq(e, arg2))    : m.mk_and(a, m_util.mk_eq(e, arg2)); return BR_REWRITE2; | ||||
|         } | ||||
|     } | ||||
|     if (m.is_ite(arg1, c, t, e) && is_numeral(e, a1) && is_numeral(arg2, a2)) { | ||||
|         expr_ref a(m.mk_not(c), m); | ||||
|         switch (kind) { | ||||
|         case LE: result = a1 <= a2 ? m.mk_or(m.mk_not(c), m_util.mk_le(t, arg2)) : m.mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2; | ||||
|         case GE: result = a1 >= a2 ? m.mk_or(m.mk_not(c), m_util.mk_ge(t, arg2)) : m.mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2; | ||||
|         case EQ: result = a1 == a2 ? m.mk_or(m.mk_not(c), m.mk_eq(t, arg2))    : m.mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2; | ||||
|         case LE: result = a1 <= a2 ? m.mk_or(a, m_util.mk_le(t, arg2)) : m.mk_and(c, m_util.mk_le(t, arg2)); return BR_REWRITE2; | ||||
|         case GE: result = a1 >= a2 ? m.mk_or(a, m_util.mk_ge(t, arg2)) : m.mk_and(c, m_util.mk_ge(t, arg2)); return BR_REWRITE2; | ||||
|         case EQ: result = a1 == a2 ? m.mk_or(a, m.mk_eq(t, arg2))    : m.mk_and(c, m_util.mk_eq(t, arg2)); return BR_REWRITE2; | ||||
|         } | ||||
|     } | ||||
|     if (m.is_ite(arg1, c, t, e) && arg1->get_ref_count() == 1) { | ||||
|         switch (kind) { | ||||
|         case LE: result = m.mk_ite(c, m_util.mk_le(t, arg2), m_util.mk_le(e, arg2)); return BR_REWRITE2; | ||||
|         case GE: result = m.mk_ite(c, m_util.mk_ge(t, arg2), m_util.mk_ge(e, arg2)); return BR_REWRITE2; | ||||
|         case EQ: result = m.mk_ite(c, m.mk_eq(t, arg2), m.mk_eq(e, arg2)); return BR_REWRITE2; | ||||
|         case LE: | ||||
|         { | ||||
|             auto a = m_util.mk_le(t, arg2); | ||||
|             result = m.mk_ite(c, a, m_util.mk_le(e, arg2)); return BR_REWRITE2; | ||||
|         } | ||||
|         case GE: { | ||||
|             auto a = m_util.mk_ge(t, arg2); | ||||
|             result = m.mk_ite(c, a, m_util.mk_ge(e, arg2)); return BR_REWRITE2; | ||||
|         } | ||||
|         case EQ:{ | ||||
|             auto a = m.mk_eq(t, arg2); | ||||
|             result = m.mk_ite(c, a, m.mk_eq(e, arg2)); return BR_REWRITE2; | ||||
|         } | ||||
|         } | ||||
|     } | ||||
|     if (m_util.is_to_int(arg2) && is_numeral(arg1)) {         | ||||
|  |  | |||
|  | @ -55,7 +55,8 @@ expr_ref sym_expr::accept(expr* e) { | |||
|             result = m.mk_bool_val((r1 <= r2) && (r2 <= r3));             | ||||
|         } | ||||
|         else { | ||||
|             result = m.mk_and(u.mk_le(m_t, e), u.mk_le(e, m_s)); | ||||
|             auto a = u.mk_le(m_t, e); | ||||
|             result = m.mk_and(a, u.mk_le(e, m_s)); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|  | @ -190,7 +191,9 @@ br_status seq_rewriter::mk_eq_helper(expr* a, expr* b, expr_ref& result) { | |||
|     // sa in (ra n rb) u (C(ra) n C(rb))
 | ||||
|     if (is_not) | ||||
|         rb = re().mk_complement(rb); | ||||
|     expr* r = re().mk_union(re().mk_inter(ra, rb), re().mk_inter(re().mk_complement(ra), re().mk_complement(rb))); | ||||
|     auto a_ = re().mk_inter(ra, rb); | ||||
|     auto b_ = re().mk_complement(ra); | ||||
|     expr* r = re().mk_union(a_, re().mk_inter(b_, re().mk_complement(rb))); | ||||
|     result = re().mk_in_re(sa, r); | ||||
|     return BR_REWRITE_FULL; | ||||
| } | ||||
|  | @ -620,10 +623,14 @@ expr_ref seq_rewriter::mk_seq_rest(expr* t) { | |||
|     expr_ref result(m()); | ||||
|     expr* s, * j, * k; | ||||
|     rational jv; | ||||
|     if (str().is_extract(t, s, j, k) &&  m_autil.is_numeral(j, jv) && jv >= 0)  | ||||
|         result = str().mk_substr(s, m_autil.mk_int(jv + 1), mk_sub(k, 1)); | ||||
|     else  | ||||
|         result = str().mk_substr(t, one(), mk_sub(str().mk_length(t), 1)); | ||||
|     if (str().is_extract(t, s, j, k) &&  m_autil.is_numeral(j, jv) && jv >= 0) { | ||||
|         auto a = m_autil.mk_int(jv + 1); | ||||
|         result = str().mk_substr(s, a, mk_sub(k, 1)); | ||||
|     } | ||||
|     else { | ||||
|         auto a = one(); | ||||
|         result = str().mk_substr(t, a, mk_sub(str().mk_length(t), 1)); | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
|  | @ -654,7 +661,10 @@ expr_ref seq_rewriter::mk_seq_last(expr* t) { | |||
| *  No: if k > |s| then substring(s,0,k) = substring(s,0,k-1) | ||||
| */ | ||||
| expr_ref seq_rewriter::mk_seq_butlast(expr* t) { | ||||
|     return expr_ref(str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one())), m()); | ||||
|     auto b = zero(); | ||||
|     auto c = str().mk_length(t); | ||||
|     auto a = str().mk_substr(t, b, m_autil.mk_sub(c, one())); | ||||
|     return expr_ref(a, m()); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -1374,9 +1384,16 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) { | |||
|     }     | ||||
| 
 | ||||
|     expr* la = str().mk_length(a); | ||||
|     result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))),  | ||||
|                         str().mk_nth_i(a, b), | ||||
|                         str().mk_nth_u(a, b)); | ||||
|     { | ||||
|         // deterministic evaluation order for guard components
 | ||||
|         auto ge0 = m_autil.mk_ge(b, zero()); | ||||
|         auto le_la = m_autil.mk_le(la, b); | ||||
|         auto not_le = m().mk_not(le_la); | ||||
|         auto guard = m().mk_and(ge0, not_le); | ||||
|         auto t1 = str().mk_nth_i(a, b); | ||||
|         auto e1 = str().mk_nth_u(a, b); | ||||
|         result = m().mk_ite(guard, t1, e1); | ||||
|     } | ||||
|     return BR_REWRITE_FULL; | ||||
| } | ||||
| 
 | ||||
|  | @ -1547,17 +1564,20 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result | |||
|     } | ||||
| 
 | ||||
|     if (str().is_empty(b)) { | ||||
|         result = m().mk_ite(m().mk_and(m_autil.mk_le(zero(), c), | ||||
|                                        m_autil.mk_le(c, str().mk_length(a))), | ||||
|                             c, | ||||
|                             minus_one()); | ||||
|         // enforce deterministic evaluation order for bounds checks
 | ||||
|         auto a1 = m_autil.mk_le(zero(), c); | ||||
|         auto b1 = m_autil.mk_le(c, str().mk_length(a)); | ||||
|         auto cond = m().mk_and(a1, b1); | ||||
|         result = m().mk_ite(cond, c, minus_one()); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     if (str().is_empty(a)) { | ||||
|         expr* emp = str().mk_is_empty(b); | ||||
|         result = m().mk_ite(m().mk_and(m().mk_eq(c, zero()), emp), zero(), minus_one()); | ||||
|         auto a1 = m().mk_eq(c, zero()); | ||||
|         auto cond = m().mk_and(a1, emp); | ||||
|         result = m().mk_ite(cond, zero(), minus_one()); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1870,7 +1890,10 @@ br_status seq_rewriter::mk_seq_map(expr* f, expr* seqA, expr_ref& result) { | |||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     if (str().is_concat(seqA, s1, s2)) { | ||||
|         result = str().mk_concat(str().mk_map(f, s1), str().mk_map(f, s2)); | ||||
|         // introduce temporaries to ensure deterministic evaluation order of recursive map calls
 | ||||
|         auto m1 = str().mk_map(f, s1); | ||||
|         auto m2 = str().mk_map(f, s2); | ||||
|         result = str().mk_concat(m1, m2); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     return BR_FAILED; | ||||
|  | @ -1890,7 +1913,9 @@ br_status seq_rewriter::mk_seq_mapi(expr* f, expr* i, expr* seqA, expr_ref& resu | |||
|     } | ||||
|     if (str().is_concat(seqA, s1, s2)) { | ||||
|         expr_ref j(m_autil.mk_add(i, str().mk_length(s1)), m()); | ||||
|         result = str().mk_concat(str().mk_mapi(f, i, s1), str().mk_mapi(f, j, s2)); | ||||
|         auto left  = str().mk_mapi(f, i, s1); | ||||
|         auto right = str().mk_mapi(f, j, s2); | ||||
|         result = str().mk_concat(left, right); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     return BR_FAILED; | ||||
|  | @ -2046,8 +2071,8 @@ br_status seq_rewriter::mk_seq_prefix(expr* a, expr* b, expr_ref& result) { | |||
|                 SASSERT(bs.size() > 1); | ||||
|                 s1 = s1.extract(s2.length(), s1.length() - s2.length()); | ||||
|                 as[0] = str().mk_string(s1); | ||||
|                 result = str().mk_prefix(str().mk_concat(as.size(), as.data(), sort_a), | ||||
|                                               str().mk_concat(bs.size()-1, bs.data()+1, sort_a)); | ||||
|                 auto a = str().mk_concat(as.size(), as.data(), sort_a); | ||||
|                 result = str().mk_prefix(a, str().mk_concat(bs.size()-1, bs.data()+1, sort_a)); | ||||
|                 TRACE(seq, tout << s1 << " " << s2 << " " << result << "\n";); | ||||
|                 return BR_REWRITE_FULL;                 | ||||
|             } | ||||
|  | @ -2384,7 +2409,8 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { | |||
|     } | ||||
|     expr* b; | ||||
|     if (str().is_itos(a, b)) { | ||||
|         result = m().mk_ite(m_autil.mk_ge(b, zero()), b, minus_one()); | ||||
|         auto a = m_autil.mk_ge(b, zero()); | ||||
|         result = m().mk_ite(a, b, minus_one()); | ||||
|         return BR_DONE; | ||||
|     } | ||||
|     if (str().is_ubv2s(a, b)) { | ||||
|  | @ -2395,7 +2421,8 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { | |||
|      | ||||
|     expr* c = nullptr, *t = nullptr, *e = nullptr; | ||||
|     if (m().is_ite(a, c, t, e)) { | ||||
|         result = m().mk_ite(c, str().mk_stoi(t), str().mk_stoi(e)); | ||||
|         auto a = str().mk_stoi(t); | ||||
|         result = m().mk_ite(c, a, str().mk_stoi(e)); | ||||
|         return BR_REWRITE_FULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -2703,7 +2730,10 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) { | |||
|     zstring zs; | ||||
|     unsigned lo = 0, hi = 0; | ||||
|     if (re().is_concat(r, r1, r2)) { | ||||
|         result = re().mk_concat(re().mk_reverse(r2), re().mk_reverse(r1)); | ||||
|         // deterministic evaluation order for reverse operands
 | ||||
|         auto a_rev = re().mk_reverse(r2); | ||||
|         auto b_rev = re().mk_reverse(r1); | ||||
|         result = re().mk_concat(a_rev, b_rev); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     else if (re().is_star(r, r1)) { | ||||
|  | @ -2715,15 +2745,22 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) { | |||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     else if (re().is_union(r, r1, r2)) { | ||||
|         result = re().mk_union(re().mk_reverse(r1), re().mk_reverse(r2)); | ||||
|         // ensure deterministic evaluation order of parameters
 | ||||
|         auto a = re().mk_reverse(r1); | ||||
|         auto b = re().mk_reverse(r2); | ||||
|         result = re().mk_union(a, b); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     else if (re().is_intersection(r, r1, r2)) { | ||||
|         result = re().mk_inter(re().mk_reverse(r1), re().mk_reverse(r2)); | ||||
|         auto a = re().mk_reverse(r1); | ||||
|         auto b = re().mk_reverse(r2); | ||||
|         result = re().mk_inter(a, b); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     else if (re().is_diff(r, r1, r2)) { | ||||
|         result = re().mk_diff(re().mk_reverse(r1), re().mk_reverse(r2)); | ||||
|         auto a = re().mk_reverse(r1); | ||||
|         auto b = re().mk_reverse(r2); | ||||
|         result = re().mk_diff(a, b); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     else if (m().is_ite(r, p, r1, r2)) { | ||||
|  | @ -2767,8 +2804,9 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) { | |||
|         return BR_DONE; | ||||
|     } | ||||
|     else if (re().is_to_re(r, s) && str().is_concat(s, s1, s2)) { | ||||
|         result = re().mk_concat(re().mk_reverse(re().mk_to_re(s2)),  | ||||
|                                 re().mk_reverse(re().mk_to_re(s1))); | ||||
|         auto a_rev = re().mk_reverse(re().mk_to_re(s2)); | ||||
|         auto b_rev = re().mk_reverse(re().mk_to_re(s1)); | ||||
|         result = re().mk_concat(a_rev, b_rev); | ||||
|         return BR_REWRITE3; | ||||
|     } | ||||
|     else { | ||||
|  | @ -2957,7 +2995,11 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref | |||
|         } | ||||
|         else { | ||||
|             // observe that the precondition |r1|>0 is is implied by c1 for use of mk_seq_first
 | ||||
|             m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_first(r1), e), c1); | ||||
|             { | ||||
|                 auto is_non_empty = m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))); | ||||
|                 auto eq_first = m().mk_eq(mk_seq_first(r1), e); | ||||
|                 m_br.mk_and(is_non_empty, eq_first, c1); | ||||
|             } | ||||
|             m_br.mk_and(path, c1, c2); | ||||
|             if (m().is_false(c2)) | ||||
|                 result = nothing(); | ||||
|  | @ -2970,7 +3012,11 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref | |||
|         if (re().is_to_re(r2, r1)) { | ||||
|             // here r1 is a sequence
 | ||||
|             // observe that the precondition |r1|>0 of mk_seq_last is implied by c1
 | ||||
|             m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_last(r1), e), c1); | ||||
|             { | ||||
|                 auto is_non_empty = m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))); | ||||
|                 auto eq_last = m().mk_eq(mk_seq_last(r1), e); | ||||
|                 m_br.mk_and(is_non_empty, eq_last, c1); | ||||
|             } | ||||
|             m_br.mk_and(path, c1, c2); | ||||
|             if (m().is_false(c2)) | ||||
|                 result = nothing(); | ||||
|  | @ -3002,8 +3048,15 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref | |||
|             result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing())); | ||||
|     } | ||||
|     else if (m().is_ite(r, c, r1, r2)) { | ||||
|         c1 = simplify_path(e, m().mk_and(c, path)); | ||||
|         c2 = simplify_path(e, m().mk_and(m().mk_not(c), path)); | ||||
|         { | ||||
|             auto cp = m().mk_and(c, path); | ||||
|             c1 = simplify_path(e, cp); | ||||
|         } | ||||
|         { | ||||
|             auto notc = m().mk_not(c); | ||||
|             auto np = m().mk_and(notc, path); | ||||
|             c2 = simplify_path(e, np); | ||||
|         } | ||||
|         if (m().is_false(c1)) | ||||
|             result = mk_antimirov_deriv(e, r2, c2); | ||||
|         else if (m().is_false(c2)) | ||||
|  | @ -3018,7 +3071,11 @@ void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref | |||
|             // SASSERT(u().is_char(c1));
 | ||||
|             // SASSERT(u().is_char(c2));
 | ||||
|             // case: c1 <= e <= c2
 | ||||
|             range = simplify_path(e, m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2))); | ||||
|             // deterministic evaluation for range bounds
 | ||||
|             auto a_le = u().mk_le(c1, e); | ||||
|             auto b_le = u().mk_le(e, c2); | ||||
|             auto rng_cond = m().mk_and(a_le, b_le); | ||||
|             range = simplify_path(e, rng_cond); | ||||
|             psi = simplify_path(e, m().mk_and(path, range)); | ||||
|         } | ||||
|         else if (!str().is_string(r1) && str().is_unit_string(r2, c2)) { | ||||
|  | @ -3399,12 +3456,22 @@ expr_ref seq_rewriter::mk_regex_reverse(expr* r) { | |||
|         result = mk_regex_concat(mk_regex_reverse(r2), mk_regex_reverse(r1)); | ||||
|     else if (m().is_ite(r, c, r1, r2)) | ||||
|         result = m().mk_ite(c, mk_regex_reverse(r1), mk_regex_reverse(r2)); | ||||
|     else if (re().is_union(r, r1, r2)) | ||||
|         result = re().mk_union(mk_regex_reverse(r1), mk_regex_reverse(r2)); | ||||
|     else if (re().is_intersection(r, r1, r2)) | ||||
|         result = re().mk_inter(mk_regex_reverse(r1), mk_regex_reverse(r2)); | ||||
|     else if (re().is_diff(r, r1, r2)) | ||||
|         result = re().mk_diff(mk_regex_reverse(r1), mk_regex_reverse(r2)); | ||||
|     else if (re().is_union(r, r1, r2)) { | ||||
|         // enforce deterministic evaluation order
 | ||||
|         auto a1 = mk_regex_reverse(r1); | ||||
|         auto b1 = mk_regex_reverse(r2); | ||||
|         result = re().mk_union(a1, b1); | ||||
|     } | ||||
|     else if (re().is_intersection(r, r1, r2)) { | ||||
|         auto a1 = mk_regex_reverse(r1); | ||||
|         auto b1 = mk_regex_reverse(r2); | ||||
|         result = re().mk_inter(a1, b1); | ||||
|     } | ||||
|     else if (re().is_diff(r, r1, r2)) { | ||||
|         auto a1 = mk_regex_reverse(r1); | ||||
|         auto b1 = mk_regex_reverse(r2); | ||||
|         result = re().mk_diff(a1, b1); | ||||
|     } | ||||
|     else if (re().is_star(r, r1)) | ||||
|         result = re().mk_star(mk_regex_reverse(r1)); | ||||
|     else if (re().is_plus(r, r1)) | ||||
|  | @ -3982,8 +4049,13 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) { | |||
|             // if ((isdigit ele) and (ele = (hd r1))) then (to_re (tl r1)) else []
 | ||||
|             //
 | ||||
|             hd = mk_seq_first(r1); | ||||
|             m_br.mk_and(u().mk_le(m_util.mk_char('0'), ele), u().mk_le(ele, m_util.mk_char('9')),  | ||||
|                 m().mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(hd, ele)), result); | ||||
|             // isolate nested conjunction for deterministic evaluation
 | ||||
|             auto a0 = u().mk_le(m_util.mk_char('0'), ele); | ||||
|             auto a1 = u().mk_le(ele, m_util.mk_char('9')); | ||||
|             auto a2 = m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))); | ||||
|             auto a3 = m().mk_eq(hd, ele); | ||||
|             auto inner = m().mk_and(a2, a3); | ||||
|             m_br.mk_and(a0, a1, inner, result); | ||||
|             tl = re().mk_to_re(mk_seq_rest(r1));             | ||||
|             return re_and(result, tl); | ||||
|         } | ||||
|  | @ -4017,7 +4089,10 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) { | |||
|                 // tl = rest of reverse(r2) i.e. butlast of r2
 | ||||
|                 //hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), one()));
 | ||||
|                 hd = mk_seq_last(r2); | ||||
|                 m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result); | ||||
|                 // factor nested constructor calls to enforce deterministic argument evaluation order
 | ||||
|                 auto a_non_empty = m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))); | ||||
|                 auto a_eq        = m().mk_eq(hd, ele); | ||||
|                 m_br.mk_and(a_non_empty, a_eq, result); | ||||
|                 tl = re().mk_to_re(mk_seq_butlast(r2)); | ||||
|                 return re_and(result, re().mk_reverse(tl)); | ||||
|             } | ||||
|  | @ -4302,9 +4377,11 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { | |||
|         (re().is_union(b, b1, eps) && re().is_epsilon(eps)) || | ||||
|         (re().is_union(b, eps, b1) && re().is_epsilon(eps))) | ||||
|     { | ||||
|         result = m().mk_ite(m().mk_eq(str().mk_length(a), zero()), | ||||
|             m().mk_true(), | ||||
|             re().mk_in_re(a, b1)); | ||||
|         // deterministic evaluation order: build sub-expressions first
 | ||||
|         auto len_a = str().mk_length(a); | ||||
|         auto is_empty = m().mk_eq(len_a, zero()); | ||||
|         auto in_b1 = re().mk_in_re(a, b1); | ||||
|         result = m().mk_ite(is_empty, m().mk_true(), in_b1); | ||||
|         return BR_REWRITE_FULL; | ||||
|     } | ||||
|     if (str().is_empty(a)) { | ||||
|  | @ -4334,9 +4411,10 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { | |||
|         expr_ref len_hd(m_autil.mk_int(re().min_length(hd)), m());  | ||||
|         expr_ref len_a(str().mk_length(a), m()); | ||||
|         expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m()); | ||||
|         result = m().mk_and(m_autil.mk_ge(len_a, len_hd), | ||||
|                             re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd), | ||||
|                             re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)); | ||||
|         auto ge_len = m_autil.mk_ge(len_a, len_hd); | ||||
|         auto prefix = re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd); | ||||
|         auto suffix = re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl); | ||||
|         result = m().mk_and(ge_len, prefix, suffix); | ||||
|         return BR_REWRITE_FULL; | ||||
|     } | ||||
|     if (get_re_head_tail_reversed(b, hd, tl)) { | ||||
|  | @ -4345,10 +4423,11 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { | |||
|         expr_ref len_a(str().mk_length(a), m()); | ||||
|         expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m()); | ||||
|         expr* s = nullptr; | ||||
|         result = m().mk_and(m_autil.mk_ge(len_a, len_tl), | ||||
|             re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd), | ||||
|             (re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) : | ||||
|                 re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl))); | ||||
|         auto ge_len = m_autil.mk_ge(len_a, len_tl); | ||||
|         auto prefix = re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd); | ||||
|         auto tail_seq = str().mk_substr(a, len_hd, len_tl); | ||||
|         auto tail = (re().is_to_re(tl, s) ? m().mk_eq(s, tail_seq) : re().mk_in_re(tail_seq, tl)); | ||||
|         result = m().mk_and(ge_len, prefix, tail); | ||||
|         return BR_REWRITE_FULL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -4614,11 +4693,17 @@ br_status seq_rewriter::mk_re_union(expr* a, expr* b, expr_ref& result) { | |||
| br_status seq_rewriter::mk_re_complement(expr* a, expr_ref& result) { | ||||
|     expr *e1 = nullptr, *e2 = nullptr; | ||||
|     if (re().is_intersection(a, e1, e2)) { | ||||
|         result = re().mk_union(re().mk_complement(e1), re().mk_complement(e2)); | ||||
|         // enforce deterministic evaluation order for nested complement arguments
 | ||||
|         auto a1 = re().mk_complement(e1); | ||||
|         auto b1 = re().mk_complement(e2); | ||||
|         result = re().mk_union(a1, b1); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     if (re().is_union(a, e1, e2)) { | ||||
|         result = re().mk_inter(re().mk_complement(e1), re().mk_complement(e2)); | ||||
|         // enforce deterministic evaluation order for nested complement arguments
 | ||||
|         auto a1 = re().mk_complement(e1); | ||||
|         auto b1 = re().mk_complement(e2); | ||||
|         result = re().mk_inter(a1, b1); | ||||
|         return BR_REWRITE2; | ||||
|     } | ||||
|     if (re().is_empty(a)) { | ||||
|  | @ -5011,7 +5096,9 @@ void seq_rewriter::elim_condition(expr* elem, expr_ref& cond) { | |||
|         rep.insert(elem, solution); | ||||
|         rep(cond); | ||||
|         if (!is_uninterp_const(elem)) {  | ||||
|             cond = m().mk_and(m().mk_eq(elem, solution), cond); | ||||
|             // ensure deterministic evaluation order when augmenting condition
 | ||||
|             auto eq_sol = m().mk_eq(elem, solution); | ||||
|             cond = m().mk_and(eq_sol, cond); | ||||
|         } | ||||
|     }     | ||||
|     else if (all_ranges) { | ||||
|  | @ -5074,11 +5161,16 @@ br_status seq_rewriter::reduce_re_is_empty(expr* r, expr_ref& result) { | |||
|     } | ||||
|     // Partial DNF expansion:
 | ||||
|     else if (re().is_intersection(r, r1, r2) && re().is_union(r1, r3, r4)) { | ||||
|         result = eq_empty(re().mk_union(re().mk_inter(r3, r2), re().mk_inter(r4, r2))); | ||||
|         // enforce deterministic order for nested intersections inside union
 | ||||
|         auto a1 = re().mk_inter(r3, r2); | ||||
|         auto b1 = re().mk_inter(r4, r2); | ||||
|         result = eq_empty(re().mk_union(a1, b1)); | ||||
|         return BR_REWRITE3; | ||||
|     } | ||||
|     else if (re().is_intersection(r, r1, r2) && re().is_union(r2, r3, r4)) { | ||||
|         result = eq_empty(re().mk_union(re().mk_inter(r3, r1), re().mk_inter(r4, r1))); | ||||
|         auto a1 = re().mk_inter(r3, r1); | ||||
|         auto b1 = re().mk_inter(r4, r1); | ||||
|         result = eq_empty(re().mk_union(a1, b1)); | ||||
|         return BR_REWRITE3; | ||||
|     } | ||||
|     return BR_FAILED; | ||||
|  |  | |||
|  | @ -42,8 +42,37 @@ class sls_tracker { | |||
|     struct value_score { | ||||
|         value_score() : value(unsynch_mpz_manager::mk_z(0)) {}; | ||||
|         value_score(value_score&&) noexcept = default; | ||||
|         value_score(const value_score &other) { | ||||
|             m = other.m; | ||||
|             if (other.m && !unsynch_mpz_manager::is_zero(other.value)) { | ||||
|                 m->set(value, other.value); | ||||
|             } | ||||
|             score = other.score; | ||||
|             score_prune = other.score_prune; | ||||
|             has_pos_occ = other.has_pos_occ; | ||||
|             has_neg_occ = other.has_neg_occ; | ||||
|             distance = other.distance; | ||||
|             touched = other.touched; | ||||
|         } | ||||
|         ~value_score() { if (m) m->del(value); } | ||||
|         value_score& operator=(value_score&&) = default; | ||||
|         value_score& operator=(value_score&&) noexcept = default; | ||||
|         value_score &operator=(const value_score &other) { | ||||
|             if (this != &other) { | ||||
|                 if (m) | ||||
|                     m->del(value); | ||||
|                 m = other.m; | ||||
|                 if (other.m && !unsynch_mpz_manager::is_zero(other.value)) { | ||||
|                     m->set(value, other.value); | ||||
|                 } | ||||
|                 score = other.score; | ||||
|                 score_prune = other.score_prune; | ||||
|                 has_pos_occ = other.has_pos_occ; | ||||
|                 has_neg_occ = other.has_neg_occ; | ||||
|                 distance = other.distance; | ||||
|                 touched = other.touched; | ||||
|             } | ||||
|             return *this; | ||||
|         } | ||||
|         unsynch_mpz_manager * m = nullptr; | ||||
|         mpz value; | ||||
|         double score = 0.0; | ||||
|  |  | |||
|  | @ -314,7 +314,7 @@ public: | |||
|         if (m_trim) | ||||
|             trim().assume(m_lits); | ||||
|         if (m_on_clause_eh) | ||||
|             m_on_clause_eh(m_on_clause_ctx, assumption(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data()); | ||||
|             m_on_clause_eh(m_on_clause_ctx, assumption(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data(), 0u); | ||||
|         m_lits.reset(); | ||||
|         m_proof_hint.reset(); | ||||
|         m_deps.reset(); | ||||
|  | @ -328,7 +328,7 @@ public: | |||
|         if (m_trim) | ||||
|             trim().infer(m_lits, m_proof_hint); | ||||
|         if (m_on_clause_eh) | ||||
|             m_on_clause_eh(m_on_clause_ctx, m_proof_hint, m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data()); | ||||
|             m_on_clause_eh(m_on_clause_ctx, m_proof_hint, m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data(), 0u); | ||||
|         m_lits.reset(); | ||||
|         m_proof_hint.reset(); | ||||
|         m_deps.reset(); | ||||
|  | @ -342,7 +342,7 @@ public: | |||
|         if (m_trim) | ||||
|             trim().del(m_lits); | ||||
|         if (m_on_clause_eh) | ||||
|             m_on_clause_eh(m_on_clause_ctx, del(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data()); | ||||
|             m_on_clause_eh(m_on_clause_ctx, del(), m_deps.size(), m_deps.data(), m_lits.size(), m_lits.data(), 0u); | ||||
|         m_lits.reset(); | ||||
|         m_proof_hint.reset(); | ||||
|         m_deps.reset(); | ||||
|  |  | |||
|  | @ -1226,6 +1226,7 @@ namespace nlsat { | |||
|          * https://arxiv.org/abs/2003.00409 
 | ||||
|          */ | ||||
|         void project_cdcac(polynomial_ref_vector & ps, var max_x) { | ||||
|             bool first = true; | ||||
|             if (ps.empty()) | ||||
|                 return; | ||||
| 
 | ||||
|  | @ -1244,8 +1245,6 @@ namespace nlsat { | |||
|             // Remark: after vanishing coefficients are eliminated, ps may not contain max_x anymore
 | ||||
|              | ||||
|             polynomial_ref_vector samples(m_pm); | ||||
| 
 | ||||
|              | ||||
|             if (x < max_x) | ||||
|                 cac_add_cell_lits(ps, x, samples); | ||||
| 
 | ||||
|  | @ -1256,9 +1255,18 @@ namespace nlsat { | |||
|                 } | ||||
|                 TRACE(nlsat_explain, tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n"; | ||||
|                       display(tout, ps); tout << "\n";); | ||||
|                 add_lcs(ps, x); | ||||
|                 psc_discriminant(ps, x); | ||||
|                 psc_resultant(ps, x); | ||||
|                 if (first) { // The first run is special because x is not constrained by the sample, we cannot surround it by the root functions.
 | ||||
|                     // we make the polynomials in ps delinable
 | ||||
|                     add_lcs(ps, x); | ||||
|                     psc_discriminant(ps, x); | ||||
|                     psc_resultant(ps, x); | ||||
|                     first = false; | ||||
|                 } | ||||
|                 else { | ||||
|                     add_lcs(ps, x); | ||||
|                     psc_discriminant(ps, x); | ||||
|                     psc_resultant_sample(ps, x, samples); | ||||
|                 } | ||||
|                  | ||||
|                 if (m_todo.empty()) | ||||
|                     break; | ||||
|  |  | |||
|  | @ -382,7 +382,7 @@ namespace euf { | |||
|         for (unsigned i = 0; i < n; ++i)  | ||||
|             m_clause.push_back(literal2expr(lits[i])); | ||||
|         auto hint = status2proof_hint(st); | ||||
|         m_on_clause(m_on_clause_ctx, hint, 0, nullptr, m_clause.size(), m_clause.data()); | ||||
|         m_on_clause(m_on_clause_ctx, hint, 0, nullptr, m_clause.size(), m_clause.data(), 0u); | ||||
|     } | ||||
| 
 | ||||
|     void solver::on_proof(unsigned n, literal const* lits, sat::status st) { | ||||
|  |  | |||
|  | @ -192,8 +192,19 @@ namespace smt { | |||
|         TRACE(clause_proof, tout << m_trail.size() << " " << st << " " << v << "\n";); | ||||
|         if (ctx.get_fparams().m_clause_proof) | ||||
|             m_trail.push_back(info(st, v, p)); | ||||
|         if (m_on_clause_eh)  | ||||
|             m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data()); | ||||
|         if (m_on_clause_eh) { | ||||
|             // Encode status as an integer flag for simplicity.
 | ||||
|             unsigned st_code = 0; | ||||
|             switch (st) { | ||||
|                 case status::assumption:    st_code = 1; break; | ||||
|                 case status::lemma:         st_code = 2; break; | ||||
|                 case status::th_lemma:      st_code = 3; break; | ||||
|                 case status::th_assumption: st_code = 4; break; | ||||
|                 case status::deleted:       st_code = 5; break; | ||||
|                 default:                    st_code = 0; break; | ||||
|             } | ||||
|             m_on_clause_eh(m_on_clause_ctx, p, 0, nullptr, v.size(), v.data(), st_code); | ||||
|         } | ||||
|          | ||||
|         if (m_has_log) { | ||||
|             init_pp_out(); | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ namespace user_propagator { | |||
|     typedef std::function<void(void*, callback*, unsigned)>                  pop_eh_t; | ||||
|     typedef std::function<void(void*, callback*, expr*)>                     created_eh_t; | ||||
|     typedef std::function<void(void*, callback*, expr*, unsigned, bool)>     decide_eh_t; | ||||
|     typedef std::function<void(void*, expr*, unsigned, unsigned const*, unsigned, expr* const*)>        on_clause_eh_t; | ||||
|     typedef std::function<void(void*, expr*, unsigned, unsigned const*, unsigned, expr* const*, unsigned const)>        on_clause_eh_t; | ||||
|     typedef std::function<bool(void*, callback*, expr*, expr*)>              binding_eh_t; | ||||
| 
 | ||||
|     class plugin : public decl_plugin { | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ add_executable(test-z3 | |||
|   api_polynomial.cpp | ||||
|   api_pb.cpp | ||||
|   api_datalog.cpp | ||||
|   parametric_datatype.cpp | ||||
|   arith_rewriter.cpp | ||||
|   arith_simplifier_plugin.cpp | ||||
|   ast.cpp | ||||
|  |  | |||
|  | @ -179,6 +179,7 @@ int main(int argc, char ** argv) { | |||
|     TST(api_polynomial); | ||||
|     TST(api_pb); | ||||
|     TST(api_datalog); | ||||
|     TST(parametric_datatype); | ||||
|     TST(cube_clause); | ||||
|     TST(old_interval); | ||||
|     TST(get_implied_equalities); | ||||
|  |  | |||
							
								
								
									
										122
									
								
								src/test/parametric_datatype.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/test/parametric_datatype.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,122 @@ | |||
| /*++
 | ||||
| Copyright (c) 2025 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     parametric_datatype.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Test parametric datatypes with type variables. | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Copilot 2025-10-12 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "api/z3.h" | ||||
| #include "util/util.h" | ||||
| #include <iostream> | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Test Z3_mk_polymorphic_datatype API with explicit parameters. | ||||
|  *  | ||||
|  * This test demonstrates the API that explicitly accepts type parameters. | ||||
|  */ | ||||
| static void test_polymorphic_datatype_api() { | ||||
|     std::cout << "test_polymorphic_datatype_api\n"; | ||||
|      | ||||
|     Z3_config cfg = Z3_mk_config(); | ||||
|     Z3_context ctx = Z3_mk_context(cfg); | ||||
|     Z3_del_config(cfg); | ||||
|      | ||||
|     // Create type variables alpha and beta for polymorphic datatype
 | ||||
|     Z3_symbol alpha_sym = Z3_mk_string_symbol(ctx, "alpha"); | ||||
|     Z3_symbol beta_sym = Z3_mk_string_symbol(ctx, "beta"); | ||||
|     Z3_sort alpha = Z3_mk_type_variable(ctx, alpha_sym); | ||||
|     Z3_sort beta = Z3_mk_type_variable(ctx, beta_sym); | ||||
|      | ||||
|     // Define parametric triple datatype with constructor mk-triple(first: alpha, second: beta, third: alpha)
 | ||||
|     Z3_symbol triple_name = Z3_mk_string_symbol(ctx, "triple"); | ||||
|     Z3_symbol mk_triple_name = Z3_mk_string_symbol(ctx, "mk-triple"); | ||||
|     Z3_symbol is_triple_name = Z3_mk_string_symbol(ctx, "is-triple"); | ||||
|     Z3_symbol first_name = Z3_mk_string_symbol(ctx, "first"); | ||||
|     Z3_symbol second_name = Z3_mk_string_symbol(ctx, "second"); | ||||
|     Z3_symbol third_name = Z3_mk_string_symbol(ctx, "third"); | ||||
|      | ||||
|     Z3_symbol field_names[3] = {first_name, second_name, third_name}; | ||||
|     Z3_sort field_sorts[3] = {alpha, beta, alpha};  // Use type variables
 | ||||
|     unsigned sort_refs[3] = {0, 0, 0};  // Not recursive references
 | ||||
|      | ||||
|     Z3_constructor mk_triple_con = Z3_mk_constructor( | ||||
|         ctx, mk_triple_name, is_triple_name, 3, field_names, field_sorts, sort_refs | ||||
|     ); | ||||
|      | ||||
|     // Create the parametric datatype using Z3_mk_polymorphic_datatype
 | ||||
|     Z3_constructor constructors[1] = {mk_triple_con}; | ||||
|     Z3_sort type_params[2] = {alpha, beta}; | ||||
|     Z3_sort triple = Z3_mk_polymorphic_datatype(ctx, triple_name, 2, type_params, 1, constructors); | ||||
|      | ||||
|     Z3_del_constructor(ctx, mk_triple_con); | ||||
|      | ||||
|     std::cout << "Created parametric triple datatype using Z3_mk_polymorphic_datatype\n"; | ||||
|     std::cout << "triple sort: " << Z3_sort_to_string(ctx, triple) << "\n"; | ||||
|      | ||||
|     // Now instantiate the datatype with concrete types
 | ||||
|     Z3_sort int_sort = Z3_mk_int_sort(ctx); | ||||
|     Z3_sort bool_sort = Z3_mk_bool_sort(ctx); | ||||
|      | ||||
|     // Create (triple Int Bool)
 | ||||
|     Z3_sort params_int_bool[2] = {int_sort, bool_sort}; | ||||
|     Z3_sort triple_int_bool = Z3_mk_datatype_sort(ctx, triple_name, 2, params_int_bool); | ||||
|      | ||||
|     std::cout << "Instantiated triple with Int and Bool\n"; | ||||
|     std::cout << "triple_int_bool: " << Z3_sort_to_string(ctx, triple_int_bool) << "\n"; | ||||
|      | ||||
|     // Get constructors and accessors from the instantiated datatype
 | ||||
|     Z3_func_decl mk_triple_int_bool = Z3_get_datatype_sort_constructor(ctx, triple_int_bool, 0); | ||||
|     Z3_func_decl first_int_bool = Z3_get_datatype_sort_constructor_accessor(ctx, triple_int_bool, 0, 0); | ||||
|     Z3_func_decl second_int_bool = Z3_get_datatype_sort_constructor_accessor(ctx, triple_int_bool, 0, 1); | ||||
|     Z3_func_decl third_int_bool = Z3_get_datatype_sort_constructor_accessor(ctx, triple_int_bool, 0, 2); | ||||
|      | ||||
|     std::cout << "Got constructors and accessors from instantiated datatype\n"; | ||||
|      | ||||
|     // Create a constant t : (triple Int Bool)
 | ||||
|     Z3_symbol t_sym = Z3_mk_string_symbol(ctx, "t"); | ||||
|     Z3_ast t = Z3_mk_const(ctx, t_sym, triple_int_bool); | ||||
|      | ||||
|     // Create (first t) - should be Int
 | ||||
|     Z3_ast first_t = Z3_mk_app(ctx, first_int_bool, 1, &t); | ||||
|      | ||||
|     // Create (third t) - should also be Int
 | ||||
|     Z3_ast third_t = Z3_mk_app(ctx, third_int_bool, 1, &t); | ||||
|      | ||||
|     // Create the equality (= (first t) (third t))
 | ||||
|     Z3_ast eq = Z3_mk_eq(ctx, first_t, third_t); | ||||
|      | ||||
|     std::cout << "Created term: " << Z3_ast_to_string(ctx, eq) << "\n"; | ||||
|      | ||||
|     // Verify the term was created successfully
 | ||||
|     ENSURE(eq != nullptr); | ||||
|      | ||||
|     // Check that first_t and third_t have the same sort (Int)
 | ||||
|     Z3_sort first_t_sort = Z3_get_sort(ctx, first_t); | ||||
|     Z3_sort third_t_sort = Z3_get_sort(ctx, third_t); | ||||
|      | ||||
|     std::cout << "Sort of (first t): " << Z3_sort_to_string(ctx, first_t_sort) << "\n"; | ||||
|     std::cout << "Sort of (third t): " << Z3_sort_to_string(ctx, third_t_sort) << "\n"; | ||||
|      | ||||
|     // Both should be Int
 | ||||
|     ENSURE(Z3_is_eq_sort(ctx, first_t_sort, int_sort)); | ||||
|     ENSURE(Z3_is_eq_sort(ctx, third_t_sort, int_sort)); | ||||
|      | ||||
|     std::cout << "test_polymorphic_datatype_api passed!\n"; | ||||
|      | ||||
|     Z3_del_context(ctx); | ||||
| } | ||||
| 
 | ||||
| void tst_parametric_datatype() { | ||||
|     test_polymorphic_datatype_api(); | ||||
| } | ||||
|  | @ -2332,7 +2332,6 @@ bool mpz_manager<SYNCH>::is_perfect_square(mpz const & a, mpz & root) { | |||
|     set(sq_lo, 1);     | ||||
| 
 | ||||
|     bool result = false; | ||||
|     bool first = true; | ||||
|     // lo*lo <= *this < hi*hi
 | ||||
| 
 | ||||
|     // first find small interval lo*lo <= a <<= hi*hi
 | ||||
|  |  | |||
|  | @ -58,6 +58,13 @@ public: | |||
|     struct key_data { | ||||
|         Key *  m_key = nullptr; | ||||
|         Value  m_value; | ||||
|         key_data() {} | ||||
|         key_data(Key *key) : m_key(key) {} | ||||
|         key_data(Key *k, Value const &v) : m_key(k), m_value(v) {} | ||||
|         key_data(key_data &&kd) noexcept = default; | ||||
|         key_data(key_data const &kd) noexcept = default; | ||||
|         key_data &operator=(key_data const &kd)  = default; | ||||
|         key_data &operator=(key_data &&kd) = default; | ||||
|         Value const & get_value() const { return m_value; } | ||||
|         Key & get_key () const { return *m_key; } | ||||
|         unsigned hash() const { return m_key->hash(); } | ||||
|  |  | |||
|  | @ -24,6 +24,10 @@ Revision History: | |||
| #include "util/buffer.h" | ||||
| #include "util/vector.h" | ||||
| 
 | ||||
| #ifndef SINGLE_THREAD | ||||
| #include <mutex> | ||||
| #endif | ||||
| 
 | ||||
| #ifdef _WINDOWS | ||||
| #if defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) | ||||
| #include <crtdbg.h> | ||||
|  | @ -67,6 +71,10 @@ static bool g_use_std_stdout = false; | |||
| static std::ostream* g_error_stream = nullptr; | ||||
| static std::ostream* g_warning_stream = nullptr; | ||||
| 
 | ||||
| #ifndef SINGLE_THREAD | ||||
| static std::mutex g_warning_mutex; | ||||
| #endif | ||||
| 
 | ||||
| void send_warnings_to_stdout(bool flag) { | ||||
|     g_use_std_stdout = flag; | ||||
| } | ||||
|  | @ -129,6 +137,9 @@ void print_msg(std::ostream * out, const char* prefix, const char* msg, va_list | |||
| 
 | ||||
| void warning_msg(const char * msg, ...) { | ||||
|     if (g_warning_msgs) { | ||||
| #ifndef SINGLE_THREAD | ||||
|         std::lock_guard<std::mutex> lock(g_warning_mutex); | ||||
| #endif | ||||
|         va_list args; | ||||
|         va_start(args, msg); | ||||
|         print_msg(g_warning_stream, "WARNING: ", msg, args); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue