mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 19:52:29 +00:00 
			
		
		
		
	Merge pull request #1906 from NikolajBjorner/csp
integrate native support for recursive definitions
This commit is contained in:
		
						commit
						eef2ac0ff5
					
				
					 70 changed files with 4838 additions and 459 deletions
				
			
		|  | @ -34,7 +34,7 @@ endif() | |||
| ################################################################################ | ||||
| set(Z3_VERSION_MAJOR 4) | ||||
| set(Z3_VERSION_MINOR 8) | ||||
| set(Z3_VERSION_PATCH 2) | ||||
| set(Z3_VERSION_PATCH 3) | ||||
| set(Z3_VERSION_TWEAK 0) | ||||
| set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") | ||||
| set(Z3_FULL_VERSION_STR "${Z3_VERSION}") # Note this might be modified | ||||
|  |  | |||
|  | @ -1,5 +1,19 @@ | |||
| RELEASE NOTES | ||||
| 
 | ||||
| Version 4.8.3 | ||||
| ============= | ||||
| - New features | ||||
|   - native handling of recursive function definitions | ||||
|   - PB rounding based option for conflict resolution when reasoning about PB constraints. | ||||
| 
 | ||||
| Version 4.8.2 | ||||
| ============= | ||||
| - Post-Release.  | ||||
| 
 | ||||
| Version 4.8.1 | ||||
| ============= | ||||
| - Release. Bug-fix for 4.8.0 | ||||
| 
 | ||||
| Version 4.8.0 | ||||
| ============= | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										93
									
								
								doc/design_recfuns.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								doc/design_recfuns.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | |||
| # Design for handling recursive functions | ||||
| 
 | ||||
| Main source of inspiration is [Sutter, Köksal & Kuncak 2011], | ||||
| as implemented in Leon, but the main | ||||
| differences is that we should unroll function definitions directly from the | ||||
| inside of Z3, in a backtracking way. Termination and fairness are ensured by | ||||
| iterative-deepening on the maximum number of unrollings in a given branch. | ||||
| 
 | ||||
| ## Unfolding | ||||
| 
 | ||||
| The idea is that every function definition `f(x1…xn) := rhs[x1…xn]` is | ||||
| compiled into: | ||||
| 
 | ||||
| - a list of cases `A_f_i[x1…xn] => f(x1…xn) = rhs_i[x1…xn]`. | ||||
|   When `A_f_i[t1…tn]` becomes true in the model, `f(t1…tn)` is said to be | ||||
|   *unfolded* and the clause `A_f_i[t1…tn] => f(t1…tn) = rhs_i[t1…tn]` | ||||
|   is added as an auxiliary clause. | ||||
| - a list of constraints `Γ_f_i[x1…xn] <=> A_f_i[x1…xn]` | ||||
|   that states when `A_f_i[x1…xn]` should be true, depending on inputs `x1…xn`. | ||||
|   For every term `f(t1…tn)` present in congruence closure, we | ||||
|   immediately add all the `Γ_f_i[t1…tn] <=> A_f_i[t1…tn]` as auxiliary clauses | ||||
|   (maybe during internalization of `f(t1…tn)`?). | ||||
| 
 | ||||
| where each `A_f_i[x1…xn]` is a special new predicate representing the | ||||
| given case of `f`, and `rhs_i` does not contain any `ite`. | ||||
| We assume pattern matching has been compiled to `ite` beforehand. | ||||
| 
 | ||||
| For example, `fact(n) := if n<2 then 1 else n * fact(n-1)` is compiled into: | ||||
| 
 | ||||
| - `A_fact_0[n] => fact(n) = 1` | ||||
| - `A_fact_1[n] => fact(n) = n * fact(n-1)` | ||||
| - `A_fact_0[n] <=> n < 2` | ||||
| - `A_fact_1[n] <=> ¬(n < 2)` | ||||
| 
 | ||||
| The 2 first clauses are only added when `A_fact_0[t]` is true | ||||
| (respectively `A_fact_1[t]` is true). | ||||
| The 2 other clauses are added as soon as `fact(t)` is internalized. | ||||
| 
 | ||||
| ## Termination | ||||
| 
 | ||||
| To ensure termination, we define variables: | ||||
| 
 | ||||
| - `unfold_depth: int` | ||||
| - `current_max_unfold_depth: int` | ||||
| - `global_max_unfold_depth: int` | ||||
| 
 | ||||
| and a special literal `[max_depth=$n]` for each `n:int`. | ||||
| Solving is done under the local assumption | ||||
| `[max_depth=$current_max_unfold_depth]` (this should be handled in some outer | ||||
| loop, e.g. in a custom tactic). | ||||
| 
 | ||||
| Whenever `A_f_i[t1…tn]` becomes true (for any `f`), we increment | ||||
| `unfold_depth`. If `unfold_depth > current_max_unfold_depth`, then | ||||
| the conflict clause `[max_depth=$current_max_unfold_depth] => Γ => false` | ||||
| where `Γ` is the conjunction of all `A_f_i[t1…tn]` true in the trail. | ||||
| 
 | ||||
| For non-recursive functions, we don't have to increment `unfold_depth`. Some other functions that are known | ||||
| 
 | ||||
| If the solver answers "SAT", we have a model. | ||||
| Otherwise, if `[max_depth=$current_max_unfold_depth]` is part of the | ||||
| unsat-core, then we increase `current_max_unfold_depth`. | ||||
| If `current_max_unfold_depth == global_max_unfold_depth` then | ||||
| we report "UNKNOWN" (reached global depth limit), otherwise we can | ||||
| try to `solve()` again with the new assumption (higher depth limit). | ||||
| 
 | ||||
| ## Tactic | ||||
| 
 | ||||
| there should be a parametrized tactic `funrec(t, n)` where `t` is the tactic | ||||
| used to solve (under assumption that depth is limited to `current_max_unfold_depth`) | ||||
| and `n` is an integer that is assigned to `global_max_unfold_depth`. | ||||
| 
 | ||||
| This way, to try and find models for a problem with recursive functions + LIA, | ||||
| one could use something like `(funrec (then simplify dl smt) 100)`. | ||||
| 
 | ||||
| ## Expected benefits | ||||
| 
 | ||||
| This addition to Z3 would bring many benefits compared to current alternatives (Leon, quantifiers, …) | ||||
| 
 | ||||
| - should be very fast and lightweight | ||||
|   (compared to Leon or quantifiers). | ||||
|   In particular, every function call is very lightweight even compared to Leon (no need for full model building, followed by unsat core extraction) | ||||
| - possibility of answering "SAT" for any `QF_*` fragment + | ||||
|   recursive functions | ||||
| - makes `define-funs-rec` a first-class citizen of the language,  usable to model user-defined theories or to analyze functional | ||||
|   programs directly | ||||
| 
 | ||||
| ## Optimizations | ||||
| 
 | ||||
| - maybe `C_f_i` literals should never be decided on | ||||
|   (they can always be propagated). | ||||
|   Even stronger: they should not be part of conflicts? | ||||
|   (i.e. tune conflict resolution to always resolve | ||||
|    these literals away, disregarding their level) | ||||
|  | @ -1190,6 +1190,20 @@ void mk_model_example() { | |||
|     std::cout << m.eval(a + b < 2)<< std::endl; | ||||
| } | ||||
| 
 | ||||
| void recfun_example() { | ||||
|     std::cout << "recfun example\n"; | ||||
|     context c;     | ||||
|     expr x = c.int_const("x"); | ||||
|     expr y = c.int_const("y"); | ||||
|     expr b = c.bool_const("b"); | ||||
|     sort I = c.int_sort(); | ||||
|     sort B = c.bool_sort();     | ||||
|     func_decl f = recfun("f", I, B, I); | ||||
|     expr_vector args(c); | ||||
|     args.push_back(x); args.push_back(b); | ||||
|     c.recdef(f, args, ite(b, x, f(x + 1, !b))); | ||||
|     prove(f(x,c.bool_val(false)) > x); | ||||
| } | ||||
| 
 | ||||
| int main() { | ||||
| 
 | ||||
|  | @ -1239,6 +1253,7 @@ int main() { | |||
|         consequence_example(); std::cout << "\n"; | ||||
|         parse_example(); std::cout << "\n"; | ||||
|         mk_model_example(); std::cout << "\n"; | ||||
|         recfun_example(); std::cout << "\n"; | ||||
|         std::cout << "done\n"; | ||||
|     } | ||||
|     catch (exception & ex) { | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ from mk_util import * | |||
| 
 | ||||
| # Z3 Project definition | ||||
| def init_project_def(): | ||||
|     set_version(4, 8, 2, 0) | ||||
|     set_version(4, 8, 3, 0) | ||||
|     add_lib('util', [], includes2install = ['z3_version.h']) | ||||
|     add_lib('polynomial', ['util'], 'math/polynomial') | ||||
|     add_lib('sat', ['util']) | ||||
|  |  | |||
|  | @ -38,6 +38,8 @@ Revision History: | |||
| #include "util/cancel_eh.h" | ||||
| #include "util/scoped_timer.h" | ||||
| #include "ast/pp_params.hpp" | ||||
| #include "ast/expr_abstract.h" | ||||
| 
 | ||||
| 
 | ||||
| extern bool is_numeral_sort(Z3_context c, Z3_sort ty); | ||||
| 
 | ||||
|  | @ -110,6 +112,54 @@ extern "C" { | |||
|         Z3_CATCH_RETURN(nullptr); | ||||
|     } | ||||
| 
 | ||||
|     Z3_func_decl Z3_API Z3_mk_rec_func_decl(Z3_context c, Z3_symbol s, unsigned domain_size, Z3_sort const* domain, | ||||
|                                             Z3_sort range) { | ||||
|         Z3_TRY; | ||||
|         LOG_Z3_mk_rec_func_decl(c, s, domain_size, domain, range); | ||||
|         RESET_ERROR_CODE(); | ||||
|         // 
 | ||||
|         recfun::promise_def def =  | ||||
|             mk_c(c)->recfun().get_plugin().mk_def(to_symbol(s),                                       | ||||
|                                           domain_size, | ||||
|                                           to_sorts(domain), | ||||
|                                           to_sort(range)); | ||||
|         func_decl* d = def.get_def()->get_decl(); | ||||
|         mk_c(c)->save_ast_trail(d); | ||||
|         RETURN_Z3(of_func_decl(d)); | ||||
|         Z3_CATCH_RETURN(nullptr); | ||||
|     } | ||||
| 
 | ||||
|     void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body) { | ||||
|         Z3_TRY; | ||||
|         LOG_Z3_add_rec_def(c, f, n, args, body); | ||||
|         func_decl* d = to_func_decl(f); | ||||
|         ast_manager& m = mk_c(c)->m(); | ||||
|         recfun::decl::plugin& p = mk_c(c)->recfun().get_plugin(); | ||||
|         expr_ref abs_body(m); | ||||
|         expr_ref_vector _args(m); | ||||
|         var_ref_vector _vars(m); | ||||
|         for (unsigned i = 0; i < n; ++i) { | ||||
|             _args.push_back(to_expr(args[i])); | ||||
|             _vars.push_back(m.mk_var(n - i - 1, m.get_sort(_args.back()))); | ||||
|             if (m.get_sort(_args.back()) != d->get_domain(i)) { | ||||
|                 SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);             | ||||
|                 return;             | ||||
|             } | ||||
|         } | ||||
|         expr_abstract(m, 0, n, _args.c_ptr(), to_expr(body), abs_body); | ||||
|         recfun::promise_def pd = p.get_promise_def(d); | ||||
|         if (!pd.get_def()) { | ||||
|             SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); | ||||
|             return; | ||||
|         } | ||||
|         if (m.get_sort(abs_body) != d->get_range()) { | ||||
|             SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);             | ||||
|             return; | ||||
|         } | ||||
|         p.set_definition(pd, n, _vars.c_ptr(), abs_body); | ||||
|         Z3_CATCH; | ||||
|     } | ||||
| 
 | ||||
|     Z3_ast Z3_API Z3_mk_app(Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const * args) { | ||||
|         Z3_TRY; | ||||
|         LOG_Z3_mk_app(c, d, num_args, args); | ||||
|  |  | |||
|  | @ -79,6 +79,7 @@ namespace api { | |||
|         m_datalog_util(m()), | ||||
|         m_fpa_util(m()), | ||||
|         m_sutil(m()), | ||||
|         m_recfun(m()), | ||||
|         m_last_result(m()), | ||||
|         m_ast_trail(m()), | ||||
|         m_pmanager(m_limit) { | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ Revision History: | |||
| #include "ast/datatype_decl_plugin.h" | ||||
| #include "ast/dl_decl_plugin.h" | ||||
| #include "ast/fpa_decl_plugin.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| #include "smt/smt_kernel.h" | ||||
| #include "smt/params/smt_params.h" | ||||
| #include "util/event_handler.h" | ||||
|  | @ -62,6 +63,7 @@ namespace api { | |||
|         datalog::dl_decl_util      m_datalog_util; | ||||
|         fpa_util                   m_fpa_util; | ||||
|         seq_util                   m_sutil; | ||||
|         recfun::util               m_recfun; | ||||
| 
 | ||||
|         // Support for old solver API
 | ||||
|         smt_params                 m_fparams; | ||||
|  | @ -128,6 +130,7 @@ namespace api { | |||
|         fpa_util & fpautil() { return m_fpa_util; } | ||||
|         datatype_util& dtutil() { return m_dt_plugin->u(); } | ||||
|         seq_util& sutil() { return m_sutil; } | ||||
|         recfun::util& recfun() { return m_recfun; } | ||||
|         family_id get_basic_fid() const { return m_basic_fid; } | ||||
|         family_id get_array_fid() const { return m_array_fid; } | ||||
|         family_id get_arith_fid() const { return m_arith_fid; } | ||||
|  |  | |||
|  | @ -715,6 +715,4 @@ extern "C" { | |||
|         Z3_CATCH_RETURN(nullptr); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| /*++
 | ||||
| Copyright (c) Microsoft Corporation, Arive Gurfinkel 2017 | ||||
| Copyright (c) Microsoft Corporation, Arie Gurfinkel 2017 | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|  |  | |||
|  | @ -311,6 +311,13 @@ namespace z3 { | |||
|         func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); | ||||
|         func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); | ||||
| 
 | ||||
|         func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range); | ||||
|         func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range); | ||||
|         func_decl recfun(char const * name, sort const & domain, sort const & range); | ||||
|         func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); | ||||
| 
 | ||||
|         void      recdef(func_decl, expr_vector const& args, expr const& body); | ||||
| 
 | ||||
|         expr constant(symbol const & name, sort const & s); | ||||
|         expr constant(char const * name, sort const & s); | ||||
|         expr bool_const(char const * name); | ||||
|  | @ -2815,6 +2822,37 @@ namespace z3 { | |||
|         return func_decl(*this, f); | ||||
|     } | ||||
| 
 | ||||
|     inline func_decl context::recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) { | ||||
|         array<Z3_sort> args(arity); | ||||
|         for (unsigned i = 0; i < arity; i++) { | ||||
|             check_context(domain[i], range); | ||||
|             args[i] = domain[i]; | ||||
|         } | ||||
|         Z3_func_decl f = Z3_mk_rec_func_decl(m_ctx, name, arity, args.ptr(), range); | ||||
|         check_error(); | ||||
|         return func_decl(*this, f); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     inline func_decl context::recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { | ||||
|         return recfun(str_symbol(name), arity, domain, range); | ||||
|     } | ||||
| 
 | ||||
|     inline func_decl context::recfun(char const * name, sort const& d1, sort const & range) { | ||||
|         return recfun(str_symbol(name), 1, &d1, range); | ||||
|     } | ||||
| 
 | ||||
|     inline func_decl context::recfun(char const * name, sort const& d1, sort const& d2, sort const & range) { | ||||
|         sort dom[2] = { d1, d2 }; | ||||
|         return recfun(str_symbol(name), 2, dom, range); | ||||
|     } | ||||
| 
 | ||||
|     void context::recdef(func_decl f, expr_vector const& args, expr const& body) { | ||||
|         check_context(f, args); check_context(f, body); | ||||
|         array<Z3_ast> vars(args); | ||||
|         Z3_add_rec_def(f.ctx(), f, vars.size(), vars.ptr(), body); | ||||
|     } | ||||
| 
 | ||||
|     inline expr context::constant(symbol const & name, sort const & s) { | ||||
|         Z3_ast r = Z3_mk_const(m_ctx, name, s); | ||||
|         check_error(); | ||||
|  | @ -2976,6 +3014,19 @@ namespace z3 { | |||
|         return range.ctx().function(name.c_str(), domain, range); | ||||
|     } | ||||
| 
 | ||||
|     inline func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range) { | ||||
|         return range.ctx().recfun(name, arity, domain, range); | ||||
|     } | ||||
|     inline func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { | ||||
|         return range.ctx().recfun(name, arity, domain, range); | ||||
|     } | ||||
|     inline func_decl recfun(char const * name, sort const& d1, sort const & range) { | ||||
|         return range.ctx().recfun(name, d1, range); | ||||
|     } | ||||
|     inline func_decl recfun(char const * name, sort const& d1, sort const& d2, sort const & range) { | ||||
|         return range.ctx().recfun(name, d1, d2, range); | ||||
|     } | ||||
| 
 | ||||
|     inline expr select(expr const & a, expr const & i) { | ||||
|         check_context(a, i); | ||||
|         Z3_ast r = Z3_mk_select(a.ctx(), a, i); | ||||
|  |  | |||
|  | @ -532,6 +532,34 @@ namespace Microsoft.Z3 | |||
|             return new FuncDecl(this, MkSymbol(name), domain, range); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Creates a new recursive function declaration. | ||||
|         /// </summary> | ||||
|         public FuncDecl MkRecFuncDecl(string name, Sort[] domain, Sort range) | ||||
|         { | ||||
|             Debug.Assert(range != null); | ||||
|             Debug.Assert(domain.All(d => d != null)); | ||||
| 
 | ||||
|             CheckContextMatch<Sort>(domain); | ||||
|             CheckContextMatch(range); | ||||
|             return new FuncDecl(this, MkSymbol(name), domain, range, true); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Bind a definition to a recursive function declaration. | ||||
| 	/// The function must have previously been created using | ||||
| 	/// MkRecFuncDecl. The body may contain recursive uses of the function or | ||||
| 	/// other mutually recursive functions.  | ||||
|         /// </summary> | ||||
| 	public void AddRecDef(FuncDecl f, Expr[] args, Expr body)  | ||||
| 	{ | ||||
| 	    CheckContextMatch(f); | ||||
| 	    CheckContextMatch<Expr>(args); | ||||
| 	    CheckContextMatch(body); | ||||
|             IntPtr[] argsNative = AST.ArrayToNative(args); | ||||
| 	    Native.Z3_add_rec_def(nCtx, f.NativeObject, (uint)args.Length, argsNative, body.NativeObject); | ||||
| 	}	 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Creates a new function declaration. | ||||
|         /// </summary> | ||||
|  |  | |||
|  | @ -302,6 +302,15 @@ namespace Microsoft.Z3 | |||
|             Debug.Assert(range != null); | ||||
|         } | ||||
| 
 | ||||
|         internal FuncDecl(Context ctx, Symbol name, Sort[] domain, Sort range, bool is_rec) | ||||
|             : base(ctx, Native.Z3_mk_rec_func_decl(ctx.nCtx, name.NativeObject, AST.ArrayLength(domain), AST.ArrayToNative(domain), range.NativeObject)) | ||||
|         { | ||||
|             Debug.Assert(ctx != null); | ||||
|             Debug.Assert(name != null); | ||||
|             Debug.Assert(range != null); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| #if DEBUG | ||||
|         internal override void CheckNativeObject(IntPtr obj) | ||||
|         { | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| 
 | ||||
| 
 | ||||
| ############################################ | ||||
| # Copyright (c) 2012 Microsoft Corporation | ||||
| # | ||||
|  | @ -799,6 +800,49 @@ def Function(name, *sig): | |||
| def _to_func_decl_ref(a, ctx): | ||||
|     return FuncDeclRef(a, ctx) | ||||
| 
 | ||||
| def RecFunction(name, *sig): | ||||
|     """Create a new Z3 recursive with the given sorts.""" | ||||
|     sig = _get_args(sig) | ||||
|     if __debug__: | ||||
|         _z3_assert(len(sig) > 0, "At least two arguments expected") | ||||
|     arity = len(sig) - 1 | ||||
|     rng   = sig[arity] | ||||
|     if __debug__: | ||||
|         _z3_assert(is_sort(rng), "Z3 sort expected") | ||||
|     dom   = (Sort * arity)() | ||||
|     for i in range(arity): | ||||
|         if __debug__: | ||||
|             _z3_assert(is_sort(sig[i]), "Z3 sort expected") | ||||
|         dom[i] = sig[i].ast | ||||
|     ctx = rng.ctx | ||||
|     return FuncDeclRef(Z3_mk_rec_func_decl(ctx.ref(), to_symbol(name, ctx), arity, dom, rng.ast), ctx) | ||||
| 
 | ||||
| def RecAddDefinition(f, args, body): | ||||
|     """Set the body of a recursive function. | ||||
|        Recursive definitions are only unfolded during search. | ||||
|     >>> ctx = Context() | ||||
|     >>> fac = RecFunction('fac', IntSort(ctx), IntSort(ctx)) | ||||
|     >>> n = Int('n', ctx) | ||||
|     >>> RecAddDefinition(fac, n, If(n == 0, 1, n*fac(n-1))) | ||||
|     >>> simplify(fac(5)) | ||||
|     fac(5) | ||||
|     >>> s = Solver(ctx=ctx) | ||||
|     >>> s.add(fac(n) < 3) | ||||
|     >>> s.check() | ||||
|     sat | ||||
|     >>> s.model().eval(fac(5)) | ||||
|     120 | ||||
|     """ | ||||
|     if is_app(args): | ||||
|        args = [args] | ||||
|     ctx = body.ctx | ||||
|     args = _get_args(args) | ||||
|     n = len(args) | ||||
|     _args = (Ast * n)() | ||||
|     for i in range(n): | ||||
|         _args[i] = args[i].ast | ||||
|     Z3_add_rec_def(ctx.ref(), f.ast, n, _args, body.ast) | ||||
| 
 | ||||
| ######################################### | ||||
| # | ||||
| # Expressions | ||||
|  | @ -6511,8 +6555,8 @@ class Solver(Z3PPObject): | |||
|         >>> s.add(x > 0, x < 2) | ||||
|         >>> s.check() | ||||
|         sat | ||||
|         >>> s.model() | ||||
|         [x = 1] | ||||
|         >>> s.model().eval(x) | ||||
|         1 | ||||
|         >>> s.add(x < 1) | ||||
|         >>> s.check() | ||||
|         unsat | ||||
|  |  | |||
|  | @ -2081,6 +2081,7 @@ extern "C" { | |||
|                                         Z3_sort range); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Create a constant or function application. | ||||
| 
 | ||||
|  | @ -2140,6 +2141,48 @@ extern "C" { | |||
|        def_API('Z3_mk_fresh_const', AST, (_in(CONTEXT), _in(STRING), _in(SORT))) | ||||
|     */ | ||||
|     Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, Z3_string prefix, Z3_sort ty); | ||||
| 
 | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Declare a recursive function | ||||
| 
 | ||||
|        \param c logical context. | ||||
|        \param s name of the function. | ||||
|        \param domain_size number of arguments. It should be greater than 0. | ||||
|        \param domain array containing the sort of each argument. The array must contain domain_size elements.  | ||||
|        \param range sort of the constant or the return sort of the function. | ||||
| 
 | ||||
|        After declaring recursive function, it should be associated with a recursive definition #Z3_mk_rec_def. | ||||
|        The function #Z3_mk_app can be used to create a constant or function | ||||
|        application. | ||||
| 
 | ||||
|        \sa Z3_mk_app | ||||
|        \sa Z3_mk_rec_def | ||||
| 
 | ||||
|        def_API('Z3_mk_rec_func_decl', FUNC_DECL, (_in(CONTEXT), _in(SYMBOL), _in(UINT), _in_array(2, SORT), _in(SORT))) | ||||
|     */ | ||||
|     Z3_func_decl Z3_API Z3_mk_rec_func_decl(Z3_context c, Z3_symbol s, | ||||
|                                         unsigned domain_size, Z3_sort const domain[], | ||||
|                                         Z3_sort range); | ||||
| 
 | ||||
|     /**
 | ||||
|        \brief Define the body of a recursive function. | ||||
|         | ||||
|        \param c logical context. | ||||
|        \param f function declaration. | ||||
|        \param n number of arguments to the function | ||||
|        \param args constants that are used as arguments to the recursive function in the definition. | ||||
|        \param body body of the recursive function | ||||
| 
 | ||||
|        After declaring a recursive function or a collection of  mutually recursive functions, use  | ||||
|        this function to provide the definition for the recursive function. | ||||
| 
 | ||||
|        \sa Z3_mk_rec_func_decl | ||||
| 
 | ||||
|        def_API('Z3_add_rec_def', VOID, (_in(CONTEXT), _in(FUNC_DECL), _in(UINT), _in_array(2, AST), _in(AST))) | ||||
|      */ | ||||
|     void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body); | ||||
| 
 | ||||
|     /*@}*/ | ||||
| 
 | ||||
|     /** @name Propositional Logic and Equality */ | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ z3_add_component(ast | |||
|     ast_translation.cpp | ||||
|     ast_util.cpp | ||||
|     bv_decl_plugin.cpp | ||||
|     csp_decl_plugin.cpp | ||||
|     datatype_decl_plugin.cpp | ||||
|     decl_collector.cpp | ||||
|     dl_decl_plugin.cpp | ||||
|  | @ -35,6 +36,7 @@ z3_add_component(ast | |||
|     occurs.cpp | ||||
|     pb_decl_plugin.cpp | ||||
|     pp.cpp | ||||
|     recfun_decl_plugin.cpp | ||||
|     reg_decl_plugins.cpp | ||||
|     seq_decl_plugin.cpp | ||||
|     shared_occs.cpp | ||||
|  |  | |||
|  | @ -361,6 +361,9 @@ public: | |||
|     app * mk_int(int i) { | ||||
|         return mk_numeral(rational(i), true); | ||||
|     } | ||||
|     app * mk_int(rational const& r) { | ||||
|         return mk_numeral(r, true); | ||||
|     } | ||||
|     app * mk_real(int i) { | ||||
|         return mk_numeral(rational(i), false); | ||||
|     } | ||||
|  |  | |||
|  | @ -16,8 +16,8 @@ Author: | |||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| #include<sstream> | ||||
| #include<cstring> | ||||
| #include <sstream> | ||||
| #include <cstring> | ||||
| #include "ast/ast.h" | ||||
| #include "ast/ast_pp.h" | ||||
| #include "ast/ast_ll_pp.h" | ||||
|  | @ -1360,7 +1360,8 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): | |||
|     m_proof_mode(disable_proofs ? PGM_DISABLED : src.m_proof_mode), | ||||
|     m_trace_stream(src.m_trace_stream), | ||||
|     m_trace_stream_owner(false), | ||||
|     m_rec_fun(":rec-fun") { | ||||
|     m_rec_fun(":rec-fun"), | ||||
|     m_lambda_def(":lambda-def") { | ||||
|     SASSERT(!src.is_format_manager()); | ||||
|     m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); | ||||
|     init(); | ||||
|  | @ -1541,6 +1542,10 @@ void ast_manager::raise_exception(char const * msg) { | |||
|     throw ast_exception(msg); | ||||
| } | ||||
| 
 | ||||
| void ast_manager::raise_exception(std::string const&  msg) { | ||||
|     throw ast_exception(msg.c_str()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::ostream& ast_manager::display(std::ostream& out, parameter const& p) { | ||||
|     switch (p.get_kind()) { | ||||
|  |  | |||
|  | @ -707,6 +707,10 @@ public: | |||
|     func_decl * get_decl() const { return m_decl; } | ||||
|     family_id get_family_id() const { return get_decl()->get_family_id(); } | ||||
|     decl_kind get_decl_kind() const { return get_decl()->get_decl_kind(); } | ||||
|     symbol const& get_name() const { return get_decl()->get_name(); } | ||||
|     unsigned get_num_parameters() const { return get_decl()->get_num_parameters(); } | ||||
|     parameter const& get_parameter(unsigned idx) const { return get_decl()->get_parameter(idx); } | ||||
|     parameter const* get_parameters() const { return get_decl()->get_parameters(); } | ||||
|     bool is_app_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; } | ||||
|     unsigned get_num_args() const { return m_num_args; } | ||||
|     expr * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; } | ||||
|  | @ -1558,7 +1562,7 @@ public: | |||
| 
 | ||||
|     // Equivalent to throw ast_exception(msg)
 | ||||
|     Z3_NORETURN void raise_exception(char const * msg); | ||||
|     Z3_NORETURN void raise_exception(std::string const& msg) { raise_exception(msg.c_str()); } | ||||
|     Z3_NORETURN void raise_exception(std::string const& s); | ||||
| 
 | ||||
|     std::ostream& display(std::ostream& out, parameter const& p); | ||||
| 
 | ||||
|  | @ -1632,7 +1636,7 @@ public: | |||
|     bool are_distinct(expr * a, expr * b) const; | ||||
| 
 | ||||
|     bool contains(ast * a) const { return m_ast_table.contains(a); } | ||||
| 
 | ||||
|      | ||||
|     bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; } | ||||
|     bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; } | ||||
|     func_decl* get_rec_fun_decl(quantifier* q) const; | ||||
|  | @ -2497,6 +2501,7 @@ public: | |||
|     void mark(ast * n, bool flag) { if (flag) mark(n); else reset_mark(n); } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| typedef ast_ref_fast_mark<1> ast_ref_fast_mark1; | ||||
| typedef ast_ref_fast_mark<2> ast_ref_fast_mark2; | ||||
| typedef ast_ref_fast_mark1   expr_ref_fast_mark1; | ||||
|  |  | |||
|  | @ -32,5 +32,27 @@ struct mk_pp : public mk_ismt2_pp { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| //<! print vector of ASTs
 | ||||
| class mk_pp_vec { | ||||
|     ast_manager &   m; | ||||
|     ast_ref_vector  vec; | ||||
| public: | ||||
|     mk_pp_vec(unsigned len, ast ** vec0, ast_manager & m) : m(m), vec(m) { | ||||
|         for (unsigned i=0; i<len; ++i) vec.push_back(vec0[i]); | ||||
|     } | ||||
|     void display(std::ostream & out) const { | ||||
|         bool first = true; | ||||
|         for (ast* e : vec) { | ||||
|             if (first) { first = false; } else { out << " "; } | ||||
|             out << mk_pp(e, m); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| inline std::ostream& operator<<(std::ostream & out, mk_pp_vec const & pp) { | ||||
|     pp.display(out); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ Revision History: | |||
| #include "ast/ast_pp_util.h" | ||||
| #include "ast/ast_smt2_pp.h" | ||||
| #include "ast/ast_smt_pp.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| 
 | ||||
| void ast_pp_util::collect(expr* e) { | ||||
|     coll.visit(e); | ||||
|  | @ -49,7 +50,14 @@ void ast_pp_util::display_decls(std::ostream& out) { | |||
|             ast_smt2_pp(out, f, m_env) << "\n"; | ||||
|         } | ||||
|     } | ||||
|     SASSERT(coll.get_num_preds() == 0); | ||||
|     vector<std::pair<func_decl*, expr*>> recfuns; | ||||
|     recfun::util u(m); | ||||
|     func_decl_ref_vector funs = u.get_rec_funs(); | ||||
|     if (funs.empty()) return; | ||||
|     for (func_decl * f : funs) { | ||||
|         recfuns.push_back(std::make_pair(f, u.get_def(f).get_rhs())); | ||||
|     } | ||||
|     ast_smt2_pp_recdefs(out, recfuns, m_env); | ||||
| } | ||||
| 
 | ||||
| void ast_pp_util::remove_decl(func_decl* f) { | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ class ast_pp_util { | |||
| 
 | ||||
|     decl_collector      coll; | ||||
| 
 | ||||
|     ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m, false) {} | ||||
|     ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m) {} | ||||
| 
 | ||||
|     void collect(expr* e); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1163,6 +1163,33 @@ public: | |||
|         unregister_var_names(f->get_arity()); | ||||
|     } | ||||
| 
 | ||||
|     // format set of mutually recursive definitions
 | ||||
|     void operator()(vector<std::pair<func_decl*, expr*>> const& funs, format_ref & r) { | ||||
|         format_ref_vector decls(m()), bodies(m()); | ||||
|         format_ref r1(m()), r2(m()); | ||||
| 
 | ||||
|         for (auto const& p : funs) { | ||||
|             unsigned len; | ||||
|             func_decl* f = p.first; | ||||
|             expr* e = p.second; | ||||
|             format * fname = m_env.pp_fdecl_name(f, len); | ||||
|             register_var_names(f->get_arity());         | ||||
|             format * args[3]; | ||||
|             args[0] = fname; | ||||
|             args[1] = pp_var_args(f->get_arity(), f->get_domain()); | ||||
|             args[2] = m_env.pp_sort(f->get_range()); | ||||
|             decls.push_back(mk_seq1<format**, f2f>(m(), args, args+3, f2f(), ""));                              | ||||
|             process(e, r); | ||||
|             bodies.push_back(r); | ||||
|             unregister_var_names(f->get_arity()); | ||||
|         } | ||||
|         r1 = mk_seq1<format*const*, f2f>(m(), decls.begin(), decls.end(), f2f(), ""); | ||||
|         r2 = mk_seq1<format*const*, f2f>(m(), bodies.begin(), bodies.end(), f2f(), ""); | ||||
|         format * args[2]; | ||||
|         args[0] = r1; | ||||
|         args[1] = r2; | ||||
|         r = mk_seq1<format**, f2f>(m(), args, args+2, f2f(), "define-rec-funs"); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| }; | ||||
|  | @ -1275,6 +1302,16 @@ std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, | |||
|     return out; | ||||
| } | ||||
| 
 | ||||
| std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector<std::pair<func_decl*, expr*>> const& funs, smt2_pp_environment & env, params_ref const & p) { | ||||
|     ast_manager & m = env.get_manager(); | ||||
|     format_ref r(fm(m)); | ||||
|     smt2_printer pr(env, p); | ||||
|     pr(funs, r); | ||||
|     pp(out, r.get(), m, p); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix): | ||||
|     m_ast(t), | ||||
|     m_manager(m), | ||||
|  |  | |||
|  | @ -105,6 +105,8 @@ std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & e | |||
| std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); | ||||
| std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); | ||||
| std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); | ||||
| std::ostream & ast_smt2_pp_recdefs(std::ostream & out, vector<std::pair<func_decl*, expr*>> const& funs, smt2_pp_environment & env, params_ref const & p = params_ref()); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|    \brief Internal wrapper (for debugging purposes only) | ||||
|  |  | |||
|  | @ -980,14 +980,6 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned i = 0; i < decls.get_num_preds(); ++i) { | ||||
|         func_decl* d = decls.get_pred_decls()[i]; | ||||
|         if (!(*m_is_declared)(d)) { | ||||
|             smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0); | ||||
|             p(d); | ||||
|             strm << "\n"; | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     for (expr* a : m_assumptions) { | ||||
|  |  | |||
							
								
								
									
										374
									
								
								src/ast/csp_decl_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								src/ast/csp_decl_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,374 @@ | |||
| /*++
 | ||||
| Copyright (c) 2018 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     csp_decl_plugin.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Declarations used for a job-shop scheduling domain. | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2018-8-9  | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| 
 | ||||
| --*/ | ||||
| #include "ast/csp_decl_plugin.h" | ||||
| #include "ast/arith_decl_plugin.h" | ||||
| 
 | ||||
| void csp_decl_plugin::set_manager(ast_manager* m, family_id fid) { | ||||
|     decl_plugin::set_manager(m, fid); | ||||
|     m_int_sort = m_manager->mk_sort(m_manager->mk_family_id("arith"), INT_SORT); | ||||
|     m_alist_sort = m_manager->mk_sort(symbol("AList"), sort_info(m_family_id, ALIST_SORT)); | ||||
|     m_job_sort = m_manager->mk_sort(symbol("Job"), sort_info(m_family_id, JOB_SORT)); | ||||
|     m_resource_sort = m_manager->mk_sort(symbol("Resource"), sort_info(m_family_id, RESOURCE_SORT)); | ||||
|     m_manager->inc_ref(m_int_sort); | ||||
|     m_manager->inc_ref(m_resource_sort); | ||||
|     m_manager->inc_ref(m_job_sort); | ||||
|     m_manager->inc_ref(m_alist_sort); | ||||
| } | ||||
| 
 | ||||
| void csp_decl_plugin::finalize() { | ||||
|     m_manager->dec_ref(m_alist_sort); | ||||
|     m_manager->dec_ref(m_job_sort); | ||||
|     m_manager->dec_ref(m_resource_sort); | ||||
|     m_manager->dec_ref(m_int_sort); | ||||
| } | ||||
| 
 | ||||
| sort * csp_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { | ||||
|     if (num_parameters != 0) { | ||||
|         m_manager->raise_exception("no parameters expected with job-shop sort"); | ||||
|     } | ||||
|     switch (static_cast<js_sort_kind>(k)) { | ||||
|     case JOB_SORT: return m_job_sort; | ||||
|     case RESOURCE_SORT: return m_resource_sort; | ||||
|     case ALIST_SORT: return m_alist_sort; | ||||
|     default: UNREACHABLE(); return nullptr; | ||||
|     }     | ||||
| } | ||||
| 
 | ||||
| func_decl * csp_decl_plugin::mk_func_decl( | ||||
|     decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort *) { | ||||
|     symbol name; | ||||
|     sort* rng = nullptr; | ||||
|     switch (static_cast<js_op_kind>(k)) { | ||||
|     case OP_JS_JOB:        | ||||
|         check_arity(arity); | ||||
|         check_index1(num_parameters, parameters); | ||||
|         name = symbol("job"); | ||||
|         rng = m_job_sort; | ||||
|         break; | ||||
|     case OP_JS_RESOURCE: | ||||
|         check_arity(arity); | ||||
|         check_index1(num_parameters, parameters); | ||||
|         name = symbol("resource"); | ||||
|         rng = m_resource_sort; | ||||
|         break; | ||||
|     case OP_JS_RESOURCE_MAKESPAN: | ||||
|         if (arity != 1 || domain[0] != m_resource_sort) m_manager->raise_exception("makespan expects a resource argument"); | ||||
|         name = symbol("makespan"); | ||||
|         rng = m_int_sort; | ||||
|         break; | ||||
|     case OP_JS_START: | ||||
|         if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("start expects a job argument"); | ||||
|         if (num_parameters > 0) m_manager->raise_exception("no parameters"); | ||||
|         name = symbol("job-start"); | ||||
|         rng = m_int_sort; | ||||
|         break; | ||||
|     case OP_JS_END: | ||||
|         if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("resource expects a job argument"); | ||||
|         if (num_parameters > 0) m_manager->raise_exception("no parameters"); | ||||
|         name = symbol("job-end"); | ||||
|         rng = m_int_sort; | ||||
|         break; | ||||
|     case OP_JS_JOB2RESOURCE: | ||||
|         if (arity != 1 || domain[0] != m_job_sort) m_manager->raise_exception("job2resource expects a job argument"); | ||||
|         if (num_parameters > 0) m_manager->raise_exception("no parameters"); | ||||
|         name = symbol("job2resource"); | ||||
|         rng = m_resource_sort; | ||||
|         break; | ||||
|     case OP_JS_MODEL: | ||||
|         // has no parameters
 | ||||
|         // all arguments are of sort alist
 | ||||
|         name = symbol("js-model"); | ||||
|         rng = m_manager->mk_bool_sort(); | ||||
|         break; | ||||
|     case OP_JS_JOB_RESOURCE:         | ||||
|         if (arity != 6) m_manager->raise_exception("add-job-resource expects 6 arguments"); | ||||
|         if (domain[0] != m_job_sort) m_manager->raise_exception("first argument of add-job-resource expects should be a job"); | ||||
|         if (domain[1] != m_resource_sort) m_manager->raise_exception("second argument of add-job-resource expects should be a resource"); | ||||
|         if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-job-resource expects should be an integer"); | ||||
|         if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-job-resource expects should be an integer"); | ||||
|         if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-job-resource expects should be an integer"); | ||||
|         if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-job-resource should be an a list of properties"); | ||||
|         name = symbol("add-job-resource"); | ||||
|         rng = m_alist_sort; | ||||
|         break; | ||||
|     case OP_JS_RESOURCE_AVAILABLE: | ||||
|         if (arity != 6) m_manager->raise_exception("add-resource-available expects 6 arguments");         | ||||
|         if (domain[0] != m_resource_sort) m_manager->raise_exception("first argument of add-resource-available expects should be a resource"); | ||||
|         if (domain[2] != m_int_sort) m_manager->raise_exception("2nd argument of add-resource-available expects should be an integer"); | ||||
|         if (domain[2] != m_int_sort) m_manager->raise_exception("3rd argument of add-resource-available expects should be an integer"); | ||||
|         if (domain[3] != m_int_sort) m_manager->raise_exception("4th argument of add-resource-available expects should be an integer"); | ||||
|         if (domain[4] != m_int_sort) m_manager->raise_exception("5th argument of add-resource-available expects should be an integer"); | ||||
|         if (domain[5] != m_alist_sort) m_manager->raise_exception("6th argument of add-resource-available should be an a list of properties"); | ||||
|         name = symbol("add-resource-available"); | ||||
|         rng = m_alist_sort; | ||||
|         break; | ||||
|     case OP_JS_JOB_PREEMPTABLE: | ||||
|         if (arity != 1 || domain[0] != m_job_sort)  | ||||
|             m_manager->raise_exception("set-preemptable expects one argument, which is a job"); | ||||
|         name = symbol("set-preemptable"); | ||||
|         rng = m_alist_sort; | ||||
|         break; | ||||
|     case OP_JS_PROPERTIES: | ||||
|         if (arity != 0) m_manager->raise_exception("js-properties takes no arguments"); | ||||
|         for (unsigned i = 0; i < num_parameters; ++i) { | ||||
|             if (!parameters[i].is_symbol()) m_manager->raise_exception("js-properties expects a list of keyword parameters"); | ||||
|         } | ||||
|         name = symbol("js-properties"); | ||||
|         rng = m_alist_sort; | ||||
|         break; | ||||
|     case OP_JS_JOB_GOAL: | ||||
|         if (arity != 1 || domain[0] != m_job_sort)  | ||||
|             m_manager->raise_exception("add-job-goal expects one argument, which is a job"); | ||||
|         if (num_parameters != 2 || !parameters[0].is_symbol() || !parameters[1].is_int()) | ||||
|             m_manager->raise_exception("add-job-goal expects one symbol and one integer parameter"); | ||||
|         name = symbol("add-job-goal"); | ||||
|         rng = m_alist_sort; | ||||
|         break; | ||||
|     case OP_JS_OBJECTIVE: | ||||
|         if (arity != 0) | ||||
|             m_manager->raise_exception("add-optimization-objective expects no arguments"); | ||||
|         if (num_parameters != 1 || !parameters[0].is_symbol()) | ||||
|             m_manager->raise_exception("add-optimization-objective expects one symbol parameter"); | ||||
|         name = symbol("add-optimization-objective"); | ||||
|         rng = m_alist_sort; | ||||
|         break; | ||||
|     default:  | ||||
|         UNREACHABLE();  | ||||
|         return nullptr;         | ||||
|     }     | ||||
|     return m_manager->mk_func_decl(name, arity, domain, rng, func_decl_info(m_family_id, k, num_parameters, parameters)); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void csp_decl_plugin::check_arity(unsigned arity) { | ||||
|     if (arity > 0) | ||||
|         m_manager->raise_exception("csp variables use parameters only and take no arguments"); | ||||
| } | ||||
| 
 | ||||
| void csp_decl_plugin::check_index1(unsigned num_parameters, parameter const* ps) { | ||||
|     if (num_parameters != 1 || !ps[0].is_int()) | ||||
|         m_manager->raise_exception("csp variable expects a single integer parameter"); | ||||
| } | ||||
| 
 | ||||
| void csp_decl_plugin::check_index2(unsigned num_parameters, parameter const* ps) { | ||||
|     if (num_parameters != 2 || !ps[0].is_int() || !ps[1].is_int()) | ||||
|         m_manager->raise_exception("csp variable expects two integer parameters"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool csp_decl_plugin::is_value(app * e) const { | ||||
|     return is_app_of(e, m_family_id, OP_JS_JOB) || is_app_of(e, m_family_id, OP_JS_RESOURCE); | ||||
| } | ||||
| 
 | ||||
| void csp_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) { | ||||
|     if (logic == symbol("CSP")) { | ||||
|         op_names.push_back(builtin_name("job", OP_JS_JOB)); | ||||
|         op_names.push_back(builtin_name("resource", OP_JS_RESOURCE)); | ||||
|         op_names.push_back(builtin_name("makespan", OP_JS_RESOURCE_MAKESPAN)); | ||||
|         op_names.push_back(builtin_name("job-start", OP_JS_START)); | ||||
|         op_names.push_back(builtin_name("job-end", OP_JS_END)); | ||||
|         op_names.push_back(builtin_name("job2resource", OP_JS_JOB2RESOURCE)); | ||||
|         op_names.push_back(builtin_name("js-model", OP_JS_MODEL)); | ||||
|         op_names.push_back(builtin_name("add-job-resource", OP_JS_JOB_RESOURCE)); | ||||
|         op_names.push_back(builtin_name("add-resource-available", OP_JS_RESOURCE_AVAILABLE)); | ||||
|         op_names.push_back(builtin_name("set-preemptable", OP_JS_JOB_PREEMPTABLE)); | ||||
|         op_names.push_back(builtin_name("js-properties", OP_JS_PROPERTIES)); | ||||
|         op_names.push_back(builtin_name("add-job-goal", OP_JS_JOB_GOAL)); | ||||
|         op_names.push_back(builtin_name("add-optimization-objective", OP_JS_OBJECTIVE)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void csp_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) { | ||||
|     if (logic == symbol("CSP")) { | ||||
|         sort_names.push_back(builtin_name("Job", JOB_SORT)); | ||||
|         sort_names.push_back(builtin_name("Resource", RESOURCE_SORT)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| expr * csp_decl_plugin::get_some_value(sort * s) { | ||||
|     parameter p(0); | ||||
|     if (is_sort_of(s, m_family_id, JOB_SORT)) | ||||
|         return m_manager->mk_const(mk_func_decl(OP_JS_JOB, 1, &p, 0, nullptr, nullptr)); | ||||
|     if (is_sort_of(s, m_family_id, RESOURCE_SORT)) | ||||
|         return m_manager->mk_const(mk_func_decl(OP_JS_RESOURCE, 1, &p, 0, nullptr, nullptr)); | ||||
|     UNREACHABLE(); | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| csp_util::csp_util(ast_manager& m): m(m) { | ||||
|     m_fid = m.mk_family_id("csp"); | ||||
|     m_plugin = static_cast<csp_decl_plugin*>(m.get_plugin(m_fid)); | ||||
| } | ||||
| 
 | ||||
| sort* csp_util::mk_job_sort() {  | ||||
|     return m_plugin->mk_job_sort();  | ||||
| } | ||||
| 
 | ||||
| sort* csp_util::mk_resource_sort() {  | ||||
|     return m_plugin->mk_resource_sort();  | ||||
| } | ||||
| 
 | ||||
| app* csp_util::mk_job(unsigned j) { | ||||
|     parameter p(j); | ||||
|     return m.mk_const(m.mk_func_decl(m_fid, OP_JS_JOB, 1, &p, 0, (sort*const*)nullptr, nullptr)); | ||||
| } | ||||
| 
 | ||||
| unsigned csp_util::job2id(expr* j) { | ||||
|     if (is_app_of(j, m_fid, OP_JS_JOB)) { | ||||
|         return to_app(j)->get_decl()->get_parameter(0).get_int(); | ||||
|     } | ||||
|     SASSERT(is_app_of(j, m_fid, OP_JS_START) ||  | ||||
|             is_app_of(j, m_fid, OP_JS_END) || | ||||
|             is_app_of(j, m_fid, OP_JS_JOB2RESOURCE)); | ||||
|     return job2id(to_app(j)->get_arg(0)); | ||||
| } | ||||
| 
 | ||||
| app* csp_util::mk_resource(unsigned r) { | ||||
|     parameter p(r); | ||||
|     return m.mk_const(m.mk_func_decl(m_fid, OP_JS_RESOURCE, 1, &p, 0, (sort*const*)nullptr, nullptr)); | ||||
| } | ||||
| 
 | ||||
| unsigned csp_util::resource2id(expr* r) { | ||||
|     SASSERT(is_app_of(r, m_fid, OP_JS_RESOURCE)); | ||||
|     return to_app(r)->get_decl()->get_parameter(0).get_int(); | ||||
| } | ||||
| 
 | ||||
| app* csp_util::mk_start(unsigned j) {  | ||||
|     app_ref job(mk_job(j), m); | ||||
|     sort* js = m.get_sort(job); | ||||
|     return m.mk_app(m.mk_func_decl(m_fid, OP_JS_START, 0, nullptr, 1, &js, nullptr), job); | ||||
| } | ||||
|          | ||||
| app* csp_util::mk_end(unsigned j) {     | ||||
|     app_ref job(mk_job(j), m); | ||||
|     sort* js = m.get_sort(job); | ||||
|     return m.mk_app(m.mk_func_decl(m_fid, OP_JS_END, 0, nullptr, 1, &js, nullptr), job); | ||||
| } | ||||
| 
 | ||||
| app* csp_util::mk_job2resource(unsigned j) {  | ||||
|     app_ref job(mk_job(j), m); | ||||
|     sort* js = m.get_sort(job); | ||||
|     return m.mk_app(m.mk_func_decl(m_fid, OP_JS_JOB2RESOURCE, 0, nullptr, 1, &js, nullptr), job); | ||||
| } | ||||
| 
 | ||||
| app* csp_util::mk_makespan(unsigned r) { | ||||
|     app_ref resource(mk_resource(r), m); | ||||
|     sort* rs = m.get_sort(resource); | ||||
|     return m.mk_app(m.mk_func_decl(m_fid, OP_JS_RESOURCE_MAKESPAN, 0, nullptr, 1, &rs, nullptr), resource); | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_resource(expr* e, unsigned& r) { | ||||
|     return is_app_of(e, m_fid, OP_JS_RESOURCE) && (r = resource2id(e), true); | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_makespan(expr * e, unsigned& r) { | ||||
|     return is_app_of(e, m_fid, OP_JS_RESOURCE_MAKESPAN) && is_resource(to_app(e)->get_arg(0), r); | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_job(expr* e, unsigned& j) { | ||||
|     return is_app_of(e, m_fid, OP_JS_JOB) && (j = job2id(e), true); | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_job2resource(expr* e, unsigned& j) { | ||||
|     return is_app_of(e, m_fid, OP_JS_JOB2RESOURCE) && (j = job2id(e), true); | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector<symbol>& properties) { | ||||
|     if (!is_app_of(e, m_fid, OP_JS_RESOURCE_AVAILABLE)) return false; | ||||
|     res = to_app(e)->get_arg(0); | ||||
|     arith_util a(m); | ||||
|     rational r; | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(1), r) || !r.is_unsigned()) return false; | ||||
|     loadpct = r.get_unsigned(); | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false; | ||||
|     cap_time = r.get_unsigned(); | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false; | ||||
|     start = r.get_uint64(); | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false; | ||||
|     end = r.get_uint64(); | ||||
|     if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector<symbol>& properties) { | ||||
|     if (!is_app_of(e, m_fid, OP_JS_JOB_RESOURCE)) return false; | ||||
|     job = to_app(e)->get_arg(0); | ||||
|     res = to_app(e)->get_arg(1); | ||||
|     arith_util a(m); | ||||
|     rational r; | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(2), r) || !r.is_unsigned()) return false; | ||||
|     loadpct = r.get_unsigned(); | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(3), r) || !r.is_uint64()) return false; | ||||
|     capacity = r.get_uint64(); | ||||
|     if (!a.is_numeral(to_app(e)->get_arg(4), r) || !r.is_uint64()) return false; | ||||
|     end = r.get_uint64(); | ||||
|     if (!is_js_properties(to_app(e)->get_arg(5), properties)) return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_set_preemptable(expr* e, expr *& job) { | ||||
|     if (!is_app_of(e, m_fid, OP_JS_JOB_PREEMPTABLE)) return false; | ||||
|     job = to_app(e)->get_arg(0); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_js_properties(expr* e, svector<symbol>& properties) { | ||||
|     if (!is_app_of(e, m_fid, OP_JS_PROPERTIES))  | ||||
|         return false; | ||||
|     unsigned sz = to_app(e)->get_decl()->get_num_parameters(); | ||||
|     for (unsigned i = 0; i < sz; ++i) { | ||||
|         properties.push_back(to_app(e)->get_decl()->get_parameter(i).get_symbol()); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job) { | ||||
|     if (!is_app_of(e, m_fid, OP_JS_JOB_GOAL))  | ||||
|         return false; | ||||
|     SASSERT(2 == to_app(e)->get_decl()->get_num_parameters()); | ||||
|     SASSERT(1 == to_app(e)->get_num_args()); | ||||
|     symbol g = to_app(e)->get_decl()->get_parameter(0).get_symbol(); | ||||
|     level = to_app(e)->get_decl()->get_parameter(1).get_int(); | ||||
|     if (g == ":earliest-end-time" || g == "earliest-end-time") | ||||
|         goal = JS_JOB_GOAL_EARLIEST_END_TIME; | ||||
|     else if (g == ":latest-start-time" || g == "latest-start-time") | ||||
|         goal = JS_JOB_GOAL_LATEST_START_TIME; | ||||
|     else  | ||||
|         return false; | ||||
|     job = to_app(e)->get_arg(0); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool csp_util::is_objective(expr* e, js_optimization_objective& objective) { | ||||
|     if (!is_app_of(e, m_fid, OP_JS_OBJECTIVE)) | ||||
|         return false; | ||||
|     SASSERT(1 == to_app(e)->get_decl()->get_num_parameters());     | ||||
|     symbol obj = to_app(e)->get_decl()->get_parameter(0).get_symbol(); | ||||
|     if (obj == ":duration" || obj == "duration") | ||||
|         objective = JS_OBJECTIVE_DURATION; | ||||
|     else if (obj == ":priority" || obj == "priority")  | ||||
|         objective = JS_OBJECTIVE_PRIORITY; | ||||
|     else  | ||||
|         return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										162
									
								
								src/ast/csp_decl_plugin.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/ast/csp_decl_plugin.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | |||
| /*++
 | ||||
| Copyright (c) 2018 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     csp_decl_plugin.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Declarations used for a job-shop scheduling domain. | ||||
| 
 | ||||
|     The job-shop domain comprises of constants job(j), resource(r) | ||||
| 
 | ||||
|     It finds values to variables: | ||||
|     - start(j), end(j), job2resource(j) | ||||
| 
 | ||||
|     It assumes a background of: | ||||
|     - resources : Job -> Resource -> Int * LoadPct - time to run job j on resource r assuming LoadPct | ||||
|     - runtime   : Job -> Int                       - time to run job j if not associated with any resource | ||||
|     - capacity  : Resource -> Int -> LoadPct       - capacity of resource r at time t, given as sequence of time intervals | ||||
|     // assume each job has at least one resource associated with it.    
 | ||||
|     // introduce a dummy resource if needed.
 | ||||
| 
 | ||||
|     // Theory:
 | ||||
|     end(j) - start(j) = time-to-execute(j) | ||||
|     time-to-execute(j) := time-to-execute(j, resource(j))  otherwise | ||||
| 
 | ||||
|     time-to-execute(j, r) := (T - start(j))  | ||||
|             where capacity(j,r) = sum_{t = start(j)}^{T} load(loadpct(j,r), r, t) | ||||
| 
 | ||||
|     capacity(j, r) := cap where (cap, loadpct) = resources j r | ||||
|     loadpct(j, r)  := loadpct where (cap, loadpct) = resources j r | ||||
| 
 | ||||
|     load(loadpct, r, t) := min(capacity r t, loadpct) / loadpct | ||||
| 
 | ||||
|     capacity(r, t) >= sum_{j | job-on-resource(j, r, t) } min(capacity r t, loadpct(j, r)) | ||||
| 
 | ||||
|     // Macros:
 | ||||
|     job-on-resource(j, r)              := r = resource(j); | ||||
|     job-on-resource(j, r, t)           := (job-on-resource(j, r) & start(j) <= t <= end(j)); | ||||
|     start_min(j, t)                    := start(j) >= t; | ||||
|     end_max(j, t)                      := end(j) <= t; | ||||
|     job_link(j1, j2, startstart, hard) := start(j1) = start(j2); | ||||
|     job_link(j1, j2, startstart, soft) := start(j1) <= start(j2); | ||||
|     job_link(j1, j2, endend, hard)     := end(j1) =  end(j2); | ||||
|     job_link(j1, j2, endend, soft)     := end(j2) <= end(j1); | ||||
|     job_link(j1, j2, endstart, hard)   := end(j1) =  start(j2); | ||||
|     job_link(j1, j2, endstart, soft)   := end(j2) <= start(j1); | ||||
|     job_link(j1, j2, startend, hard)   := end(j2) =  start(j1); | ||||
|     job_link(j1, j2, startend, soft)   := end(j1) <= start(j2); | ||||
|     job_delay(j1, j2, t)               := end(j1) + t <= end(j2); | ||||
|     job_on_same_resource(j1, j2)       := resource(j1) = resource(j2); | ||||
|     job_not_on_same_resource(j1, j2)   := resource(j1) != resource(j2); | ||||
|     job_time_intersect(j1, j2)         := start(j1) <= end(j2) <= end(j1) || start(j2) <= end(j1) <= end(j2); | ||||
|      | ||||
|     job-on-resource(j, r, t) => job-property(j) = null or job_property(j) in working_time_property(r, t); | ||||
|          | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2018-8-9  | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| 
 | ||||
| --*/ | ||||
| #pragma once; | ||||
| #include "ast/ast.h" | ||||
| 
 | ||||
| enum js_sort_kind { | ||||
|     JOB_SORT, | ||||
|     RESOURCE_SORT, | ||||
|     ALIST_SORT | ||||
| }; | ||||
| 
 | ||||
| enum js_op_kind { | ||||
|     OP_JS_JOB,               // value of type job
 | ||||
|     OP_JS_RESOURCE,          // value of type resource
 | ||||
|     OP_JS_RESOURCE_MAKESPAN, // makespan of resource: the minimal resource time required for assigned jobs.
 | ||||
|     OP_JS_START,             // start time of a job
 | ||||
|     OP_JS_END,               // end time of a job
 | ||||
|     OP_JS_JOB2RESOURCE,      // resource associated with job
 | ||||
|     OP_JS_MODEL,             // jobscheduler model
 | ||||
|     OP_JS_JOB_RESOURCE,      // model declaration for job assignment to resource
 | ||||
|     OP_JS_JOB_PREEMPTABLE,   // model declaration for whether job is pre-emptable
 | ||||
|     OP_JS_RESOURCE_AVAILABLE, // model declaration for availability intervals of resource
 | ||||
|     OP_JS_PROPERTIES,         // model declaration of a set of properties. Each property is a keyword.
 | ||||
|     OP_JS_JOB_GOAL,           // job goal objective :earliest-end-time or :latest-start-time
 | ||||
|     OP_JS_OBJECTIVE           // duration or completion-time
 | ||||
| }; | ||||
| 
 | ||||
| enum js_job_goal { | ||||
|     JS_JOB_GOAL_EARLIEST_END_TIME, | ||||
|     JS_JOB_GOAL_LATEST_START_TIME | ||||
| }; | ||||
| 
 | ||||
| enum js_optimization_objective { | ||||
|     JS_OBJECTIVE_DURATION, | ||||
|     JS_OBJECTIVE_PRIORITY | ||||
| }; | ||||
| 
 | ||||
| class csp_decl_plugin : public decl_plugin { | ||||
| public: | ||||
|     csp_decl_plugin() {} | ||||
|     ~csp_decl_plugin() override {} | ||||
|     void finalize() override; | ||||
|     void set_manager(ast_manager* m, family_id fid) override; | ||||
|     decl_plugin * mk_fresh() override { return alloc(csp_decl_plugin); } | ||||
|     sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; | ||||
|     func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, | ||||
|                              unsigned arity, sort * const * domain, sort * range) override;	 | ||||
|     bool is_value(app * e) const override; | ||||
|     bool is_unique_value(app * e) const override { return is_value(e); } | ||||
|     void get_op_names(svector<builtin_name> & op_names, symbol const & logic) override; | ||||
|     void get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) override; | ||||
|     expr * get_some_value(sort * s) override; | ||||
|     sort * mk_job_sort() const { return m_job_sort; } | ||||
|     sort * mk_resource_sort() const { return m_resource_sort; } | ||||
|     sort * mk_alist_sort() const { return m_alist_sort; } | ||||
| private: | ||||
|     sort* m_job_sort; | ||||
|     sort* m_resource_sort; | ||||
|     sort* m_alist_sort; | ||||
|     sort* m_int_sort; | ||||
| 
 | ||||
|     void check_arity(unsigned arity); | ||||
|     void check_index1(unsigned n, parameter const* ps); | ||||
|     void check_index2(unsigned n, parameter const* ps); | ||||
| }; | ||||
| 
 | ||||
| class csp_util { | ||||
|     ast_manager&         m; | ||||
|     family_id            m_fid; | ||||
|     csp_decl_plugin*     m_plugin; | ||||
| public: | ||||
|     csp_util(ast_manager& m); | ||||
|     sort* mk_job_sort(); | ||||
|     sort* mk_resource_sort(); | ||||
| 
 | ||||
|     app* mk_job(unsigned j); | ||||
|     app* mk_resource(unsigned r); | ||||
|     app* mk_start(unsigned j); | ||||
|     app* mk_end(unsigned j); | ||||
|     app* mk_job2resource(unsigned j); | ||||
|     app* mk_makespan(unsigned r); | ||||
| 
 | ||||
|     bool is_job(expr* e, unsigned& j); | ||||
|     bool is_job2resource(expr* e, unsigned& j); | ||||
|     bool is_resource(expr* e, unsigned& r); | ||||
|     bool is_makespan(expr* e, unsigned& r); | ||||
|     bool is_add_resource_available(expr * e, expr *& res, unsigned& loadpct, unsigned& cap_time, uint64_t& start, uint64_t& end, svector<symbol>& properites); | ||||
|     bool is_add_job_resource(expr * e, expr *& job, expr*& res, unsigned& loadpct, uint64_t& capacity, uint64_t& end, svector<symbol>& properites);  | ||||
|     bool is_set_preemptable(expr* e, expr *& job); | ||||
|     bool is_model(expr* e) const { return is_app_of(e, m_fid, OP_JS_MODEL); } | ||||
|     bool is_js_properties(expr* e, svector<symbol>& properties); | ||||
|     bool is_job_goal(expr* e, js_job_goal& goal, unsigned& level, expr*& job); | ||||
|     bool is_objective(expr* e, js_optimization_objective& objective); | ||||
| 
 | ||||
| private: | ||||
|     unsigned job2id(expr* j); | ||||
|     unsigned resource2id(expr* r); | ||||
| 
 | ||||
| }; | ||||
|  | @ -50,18 +50,14 @@ void decl_collector::visit_func(func_decl * n) { | |||
|     if (!m_visited.is_marked(n)) { | ||||
|         family_id fid = n->get_family_id(); | ||||
|         if (fid == null_family_id) { | ||||
|             if (m_sep_preds && is_bool(n->get_range())) | ||||
|                 m_preds.push_back(n); | ||||
|             else | ||||
|                 m_decls.push_back(n); | ||||
|             m_decls.push_back(n); | ||||
|         } | ||||
|         m_visited.mark(n, true); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| decl_collector::decl_collector(ast_manager & m, bool preds): | ||||
| decl_collector::decl_collector(ast_manager & m): | ||||
|     m_manager(m), | ||||
|     m_sep_preds(preds), | ||||
|     m_dt_util(m) { | ||||
|     m_basic_fid = m_manager.get_basic_family_id(); | ||||
|     m_dt_fid = m_dt_util.get_family_id(); | ||||
|  |  | |||
|  | @ -26,10 +26,8 @@ Revision History: | |||
| 
 | ||||
| class decl_collector { | ||||
|     ast_manager &         m_manager; | ||||
|     bool                  m_sep_preds; | ||||
|     ptr_vector<sort>      m_sorts; | ||||
|     ptr_vector<func_decl> m_decls; | ||||
|     ptr_vector<func_decl> m_preds; | ||||
|     ast_mark              m_visited; | ||||
|     family_id             m_basic_fid; | ||||
|     family_id             m_dt_fid; | ||||
|  | @ -46,8 +44,7 @@ class decl_collector { | |||
| 
 | ||||
| 
 | ||||
| public: | ||||
|     // if preds == true, then predicates are stored in a separate collection.
 | ||||
|     decl_collector(ast_manager & m, bool preds = true); | ||||
|     decl_collector(ast_manager & m); | ||||
|     ast_manager & m() { return m_manager; } | ||||
| 
 | ||||
|     void visit_func(func_decl* n); | ||||
|  | @ -59,11 +56,9 @@ public: | |||
| 
 | ||||
|     unsigned get_num_sorts() const { return m_sorts.size(); } | ||||
|     unsigned get_num_decls() const { return m_decls.size(); } | ||||
|     unsigned get_num_preds() const { return m_preds.size(); } | ||||
|      | ||||
|     sort * const * get_sorts() const { return m_sorts.c_ptr(); } | ||||
|     func_decl * const * get_func_decls() const { return m_decls.c_ptr(); } | ||||
|     func_decl * const * get_pred_decls() const { return m_preds.c_ptr(); } | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -69,7 +69,11 @@ void check_pred::visit(expr* e) { | |||
|         case AST_QUANTIFIER: { | ||||
|             quantifier* q = to_quantifier(e); | ||||
|             expr* arg = q->get_expr(); | ||||
|             if (m_visited.is_marked(arg)) { | ||||
|             if (!m_check_quantifiers) { | ||||
|                 todo.pop_back(); | ||||
|                 m_visited.mark(e, true); | ||||
|             } | ||||
|             else if (m_visited.is_marked(arg)) { | ||||
|                 todo.pop_back(); | ||||
|                 if (m_pred_holds.is_marked(arg)) { | ||||
|                     m_pred_holds.mark(e, true); | ||||
|  |  | |||
|  | @ -53,8 +53,10 @@ class check_pred { | |||
|     ast_mark        m_pred_holds; | ||||
|     ast_mark        m_visited; | ||||
|     expr_ref_vector m_refs; | ||||
|     bool            m_check_quantifiers; | ||||
| public:         | ||||
|     check_pred(i_expr_pred& p, ast_manager& m) : m_pred(p), m_refs(m) {} | ||||
|     check_pred(i_expr_pred& p, ast_manager& m, bool check_quantifiers = true) :  | ||||
|         m_pred(p), m_refs(m), m_check_quantifiers(check_quantifiers) {} | ||||
|          | ||||
|     bool operator()(expr* e); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										428
									
								
								src/ast/recfun_decl_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										428
									
								
								src/ast/recfun_decl_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,428 @@ | |||
| /*++
 | ||||
| Copyright (c) 2017 Microsoft Corporation, Simon Cruanes | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     recfun_decl_plugin.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Declaration and definition of (potentially recursive) functions | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Simon Cruanes 2017-11 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <sstream> | ||||
| #include "ast/expr_functors.h" | ||||
| #include "ast/expr_substitution.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| #include "ast/ast_pp.h" | ||||
| #include "util/scoped_ptr_vector.h" | ||||
| 
 | ||||
| #define TRACEFN(x) TRACE("recfun", tout << x << '\n';) | ||||
| #define VALIDATE_PARAM(m, _pred_) if (!(_pred_)) m.raise_exception("invalid parameter to recfun "  #_pred_); | ||||
| 
 | ||||
| namespace recfun { | ||||
| 
 | ||||
| 
 | ||||
|     case_def::case_def( | ||||
|         ast_manager &m, | ||||
|         family_id fid, | ||||
|         def * d, | ||||
|         std::string & name, | ||||
|         unsigned case_index, | ||||
|         sort_ref_vector const & arg_sorts, | ||||
|         expr_ref_vector const& guards,  | ||||
|         expr* rhs) | ||||
|         : m_pred(m), | ||||
|           m_guards(guards), | ||||
|           m_rhs(expr_ref(rhs,m)),  | ||||
|           m_def(d) {         | ||||
|         parameter p(case_index); | ||||
|         func_decl_info info(fid, OP_FUN_CASE_PRED, 1, &p); | ||||
|         m_pred = m.mk_func_decl(symbol(name.c_str()), arg_sorts.size(), arg_sorts.c_ptr(), m.mk_bool_sort(), info); | ||||
|     } | ||||
| 
 | ||||
|     def::def(ast_manager &m, family_id fid, symbol const & s, | ||||
|              unsigned arity, sort* const * domain, sort* range) | ||||
|         :   m(m), m_name(s), | ||||
|             m_domain(m, arity, domain),  | ||||
|             m_range(range, m), m_vars(m), m_cases(), | ||||
|             m_decl(m),  | ||||
|             m_rhs(m), | ||||
|             m_fid(fid) | ||||
|     { | ||||
|         SASSERT(arity == get_arity());         | ||||
|         func_decl_info info(fid, OP_FUN_DEFINED); | ||||
|         m_decl = m.mk_func_decl(s, arity, domain, range, info); | ||||
|     } | ||||
| 
 | ||||
|     // does `e` contain any `ite` construct?
 | ||||
|     bool def::contains_ite(expr * e) { | ||||
|         struct ite_find_p : public i_expr_pred { | ||||
|             ast_manager & m; | ||||
|             ite_find_p(ast_manager & m) : m(m) {} | ||||
|             virtual bool operator()(expr * e) { return m.is_ite(e); } | ||||
|         }; | ||||
|         // ignore ites under quantifiers.
 | ||||
|         // this is redundant as the code
 | ||||
|         // that unfolds ites uses quantifier-free portion.
 | ||||
|         ite_find_p p(m); | ||||
|         check_pred cp(p, m, false); | ||||
|         return cp(e); | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * compilation of functions to a list of cases. | ||||
|      *  | ||||
|      * We use a backtracking algorithm in a relatively functional style, | ||||
|      * where the multiple states (corresponding to alternatives) are stored in | ||||
|      * a region, and deallocated at the end | ||||
|      */ | ||||
| 
 | ||||
|     // immutable list of choices of `ite` terms (mapping each one's condition to true/false)
 | ||||
|     struct choice_lst { | ||||
|         app *               ite; | ||||
|         bool                sign; | ||||
|         choice_lst const *  next; // or null for the last one
 | ||||
|         choice_lst(app * ite, bool sign, choice_lst const * next) : ite(ite), sign(sign), next(next) {} | ||||
|     }; | ||||
| 
 | ||||
|     struct ite_lst { | ||||
|         app *              ite; // invariant: `is_ite(e)`
 | ||||
|         ite_lst const *    next; | ||||
|         ite_lst(app * ite, ite_lst const * next) : ite(ite), next(next) {} | ||||
|     }; | ||||
| 
 | ||||
|     // immutable stack of expressions to unfold
 | ||||
|     struct unfold_lst { | ||||
|         expr *              e; | ||||
|         unfold_lst const *  next; // or null for last one
 | ||||
|     }; | ||||
| 
 | ||||
|     // main state for one branch of the search tree.
 | ||||
|     struct branch { | ||||
|         choice_lst const *      path; // choices made so far
 | ||||
|         ite_lst const *         to_split; // `ite` terms to make a choice on
 | ||||
|         unfold_lst const *      to_unfold; // terms yet to unfold
 | ||||
| 
 | ||||
|         branch(unfold_lst const * to_unfold): | ||||
|             path(nullptr), to_split(nullptr), to_unfold(to_unfold) {} | ||||
|         branch(choice_lst const * path, ite_lst const * to_split, unfold_lst const * to_unfold) :  | ||||
|             path(path), to_split(to_split), to_unfold(to_unfold) {} | ||||
|         branch(branch const & from) :  | ||||
|             path(from.path), to_split(from.to_split), to_unfold(from.to_unfold) {} | ||||
|     }; | ||||
| 
 | ||||
|     // state for computing cases from the RHS of a functions' definition
 | ||||
|     class case_state { | ||||
|         region              m_reg; | ||||
|         vector<branch>      m_branches; | ||||
| 
 | ||||
|     public: | ||||
|         case_state() : m_reg(), m_branches() {} | ||||
|          | ||||
|         bool empty() const { return m_branches.empty(); } | ||||
| 
 | ||||
|         branch pop_branch() { | ||||
|             branch res = m_branches.back(); | ||||
|             m_branches.pop_back(); | ||||
|             return res; | ||||
|         } | ||||
| 
 | ||||
|         void push_branch(branch const & b) { m_branches.push_back(b); } | ||||
| 
 | ||||
|         unfold_lst const * cons_unfold(expr * e, unfold_lst const * next) { | ||||
|             return new (m_reg) unfold_lst{e, next}; | ||||
|         } | ||||
|         unfold_lst const * cons_unfold(expr * e1, expr * e2, unfold_lst const * next) { | ||||
|             return cons_unfold(e1, cons_unfold(e2, next)); | ||||
|         } | ||||
|         unfold_lst const * mk_unfold_lst(expr * e) { | ||||
|             return cons_unfold(e, nullptr); | ||||
|         } | ||||
| 
 | ||||
|         ite_lst const * cons_ite(app * ite, ite_lst const * next) { | ||||
|             return new (m_reg) ite_lst{ite, next}; | ||||
|         } | ||||
| 
 | ||||
|         choice_lst const * cons_choice(app * ite, bool sign, choice_lst const * next) { | ||||
|             return new (m_reg) choice_lst{ite, sign, next}; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     //<! build a substitution and a list of conditions from a path
 | ||||
|     static void convert_path(ast_manager & m, | ||||
|                       choice_lst const * choices, | ||||
|                       expr_ref_vector & conditions /* out */, | ||||
|                       expr_substitution & subst /* out */) | ||||
|     { | ||||
|         for (; choices != nullptr; choices = choices->next) { | ||||
|             app * ite = choices->ite; | ||||
|             expr* c = nullptr, *th = nullptr, *el = nullptr; | ||||
|             VERIFY(m.is_ite(ite, c, th, el)); | ||||
| 
 | ||||
|             // condition to add to the guard
 | ||||
|             conditions.push_back(choices->sign ? c : m.mk_not(c)); | ||||
| 
 | ||||
|             // binding to add to the substitution
 | ||||
|             subst.insert(ite, choices->sign ? th : el); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // substitute `subst` in `e`
 | ||||
|     static expr_ref replace_subst(th_rewriter & th_rw, ast_manager & m, | ||||
|                                   expr_substitution & subst, expr * e) { | ||||
|         th_rw.reset(); | ||||
|         th_rw.set_substitution(&subst); | ||||
|         expr_ref res(m); | ||||
|         th_rw(e, res); | ||||
|         return res; | ||||
|     } | ||||
| 
 | ||||
|     void def::add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr * rhs, bool is_imm) { | ||||
|         case_def c(m, m_fid, this, name, case_index, get_domain(), conditions, rhs); | ||||
|         c.set_is_immediate(is_imm); | ||||
|         TRACEFN("add_case " << name << " " << mk_pp(rhs, m) | ||||
|                 << " :is_imm " << is_imm | ||||
|                 << " :guards " << conditions); | ||||
|         m_cases.push_back(c); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     // Compute a set of cases, given the RHS
 | ||||
|     void def::compute_cases(is_immediate_pred & is_i, th_rewriter & th_rw, | ||||
|                             unsigned n_vars, var *const * vars, expr* rhs) | ||||
|     { | ||||
|         VERIFY(m_cases.empty() && "cases cannot already be computed"); | ||||
|         SASSERT(n_vars == m_domain.size()); | ||||
| 
 | ||||
|         TRACEFN("compute cases " << mk_pp(rhs, m)); | ||||
| 
 | ||||
|         unsigned case_idx = 0; | ||||
| 
 | ||||
|         std::string name("case-");        | ||||
|         name.append(m_name.bare_str()); | ||||
| 
 | ||||
|         m_vars.append(n_vars, vars); | ||||
|         m_rhs = rhs; | ||||
| 
 | ||||
|         expr_ref_vector conditions(m); | ||||
| 
 | ||||
|         // is the function a macro (unconditional body)?
 | ||||
|         if (n_vars == 0 || !contains_ite(rhs)) { | ||||
|             // constant function or trivial control flow, only one (dummy) case
 | ||||
|             add_case(name, 0, conditions, rhs); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         // analyze control flow of `rhs`, accumulating guards and
 | ||||
|         // rebuilding a `ite`-free RHS on the fly for each path in `rhs`.
 | ||||
|         // Each such `ite`-free term is converted into a case_def and added to definition.
 | ||||
| 
 | ||||
|         case_state st; | ||||
| 
 | ||||
|         st.push_branch(branch(st.mk_unfold_lst(rhs)));         | ||||
| 
 | ||||
|         while (! st.empty()) { | ||||
|             TRACEFN("main loop iter"); | ||||
| 
 | ||||
|             branch b = st.pop_branch(); | ||||
| 
 | ||||
|             // first: unfold expressions, stopping when we meet subterms that are `ite`
 | ||||
|             while (b.to_unfold != nullptr) { | ||||
| 
 | ||||
|                 ptr_vector<expr> stack; | ||||
|                 stack.push_back(b.to_unfold->e); | ||||
|                  | ||||
|                 b.to_unfold = b.to_unfold->next; | ||||
| 
 | ||||
|                 while (! stack.empty()) { | ||||
|                     expr * e = stack.back(); | ||||
|                     stack.pop_back(); | ||||
| 
 | ||||
|                     if (m.is_ite(e)) { | ||||
|                         // need to do a case split on `e`, forking the search space
 | ||||
|                         b.to_split = st.cons_ite(to_app(e), b.to_split); | ||||
|                     }  | ||||
|                     else if (is_app(e)) { | ||||
|                         // explore arguments
 | ||||
|                         for (expr * arg : *to_app(e)) { | ||||
|                             if (contains_ite(arg)) { | ||||
|                                 stack.push_back(arg); | ||||
|                             } | ||||
|                         } | ||||
|                     }  | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (b.to_split != nullptr) { | ||||
|                 // split one `ite`, which will lead to distinct (sets of) cases
 | ||||
|                 app * ite = b.to_split->ite; | ||||
|                 expr* c = nullptr, *th = nullptr, *el = nullptr; | ||||
|                 VERIFY(m.is_ite(ite, c, th, el)); | ||||
| 
 | ||||
|                 /* explore both positive choice and negative choice.
 | ||||
|                  * each contains a longer path, with `ite` mapping to `true` (resp. `false), | ||||
|                  * and must unfold the `then` (resp. `else`) branch. | ||||
|                  * We must also unfold the test itself, for it could contain | ||||
|                  * tests. | ||||
|                  */ | ||||
| 
 | ||||
|                 branch b_pos(st.cons_choice(ite, true, b.path), | ||||
|                              b.to_split->next, | ||||
|                              st.cons_unfold(c, th, b.to_unfold)); | ||||
| 
 | ||||
|                 branch b_neg(st.cons_choice(ite, false, b.path), | ||||
|                              b.to_split->next, | ||||
|                              st.cons_unfold(c, el, b.to_unfold)); | ||||
| 
 | ||||
|                 st.push_branch(b_neg); | ||||
|                 st.push_branch(b_pos); | ||||
|             } | ||||
|             else { | ||||
|                 // leaf of the search tree
 | ||||
| 
 | ||||
|                 conditions.reset(); | ||||
|                 expr_substitution subst(m); | ||||
|                 convert_path(m, b.path, conditions, subst); | ||||
|                  | ||||
|                 // substitute, to get rid of `ite` terms
 | ||||
|                 expr_ref case_rhs = replace_subst(th_rw, m, subst, rhs); | ||||
|                 for (unsigned i = 0; i < conditions.size(); ++i) { | ||||
|                     conditions[i] = replace_subst(th_rw, m, subst, conditions.get(i)); | ||||
|                 } | ||||
|                  | ||||
|                 // yield new case
 | ||||
|                 bool is_imm = is_i(case_rhs); | ||||
|                 add_case(name, case_idx++, conditions, case_rhs, is_imm); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         TRACEFN("done analyzing " << get_name()); | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Main manager for defined functions | ||||
|      */ | ||||
| 
 | ||||
|     util::util(ast_manager & m) | ||||
|         : m_manager(m), m_fid(m.get_family_id("recfun")), m_th_rw(m), | ||||
|           m_plugin(dynamic_cast<decl::plugin*>(m.get_plugin(m_fid))) { | ||||
|     } | ||||
| 
 | ||||
|     util::~util() { | ||||
|     } | ||||
| 
 | ||||
|     def * util::decl_fun(symbol const& name, unsigned n, sort *const * domain, sort * range) { | ||||
|         return alloc(def, m(), m_fid, name, n, domain, range); | ||||
|     } | ||||
| 
 | ||||
|     void util::set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) { | ||||
|         d.set_definition(n_vars, vars, rhs); | ||||
|     } | ||||
| 
 | ||||
|     app_ref util::mk_depth_limit_pred(unsigned d) { | ||||
|         parameter p(d); | ||||
|         func_decl_info info(m_fid, OP_DEPTH_LIMIT, 1, &p); | ||||
|         func_decl* decl = m().mk_const_decl(symbol("recfun-depth-limit"), m().mk_bool_sort(), info); | ||||
|         return app_ref(m().mk_const(decl), m()); | ||||
|     } | ||||
| 
 | ||||
|     // used to know which `app` are from this theory
 | ||||
|     struct is_imm_pred : is_immediate_pred { | ||||
|         util & u; | ||||
|         is_imm_pred(util & u) : u(u) {} | ||||
|         bool operator()(expr * rhs) { | ||||
|             // find an `app` that is an application of a defined function
 | ||||
|             struct find : public i_expr_pred { | ||||
|                 util & u; | ||||
|                 find(util & u) : u(u) {} | ||||
|                 bool operator()(expr * e) override { | ||||
|                     //return is_app(e) ? u.owns_app(to_app(e)) : false;
 | ||||
|                     if (! is_app(e)) return false; | ||||
| 
 | ||||
|                     app * a = to_app(e); | ||||
|                     return u.is_defined(a); | ||||
|                 } | ||||
|             }; | ||||
|             find f(u); | ||||
|             check_pred cp(f, u.m()); | ||||
|             bool contains_defined_fun = cp(rhs); | ||||
|             return ! contains_defined_fun; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // set definition 
 | ||||
|     void promise_def::set_definition(unsigned n_vars, var * const * vars, expr * rhs) { | ||||
|         SASSERT(n_vars == d->get_arity()); | ||||
|                      | ||||
|         is_imm_pred is_i(*u); | ||||
|         d->compute_cases(is_i, u->get_th_rewriter(), n_vars, vars, rhs); | ||||
|     } | ||||
| 
 | ||||
|     namespace decl { | ||||
|         plugin::plugin() : decl_plugin(), m_defs(), m_case_defs() {} | ||||
|         plugin::~plugin() { finalize(); } | ||||
| 
 | ||||
|         void plugin::finalize() { | ||||
|             for (auto& kv : m_defs) { | ||||
|                 dealloc(kv.m_value); | ||||
|             } | ||||
|             m_defs.reset(); | ||||
|             // m_case_defs does not own its data, no need to deallocate
 | ||||
|             m_case_defs.reset(); | ||||
|             m_util = nullptr; // force deletion
 | ||||
|         } | ||||
| 
 | ||||
|         util & plugin::u() const { | ||||
|             SASSERT(m_manager); | ||||
|             SASSERT(m_family_id != null_family_id); | ||||
|             if (!m_util.get()) { | ||||
|                 m_util = alloc(util, *m_manager); | ||||
|             } | ||||
|             return *(m_util.get()); | ||||
|         } | ||||
| 
 | ||||
|         promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range) { | ||||
|             def* d = u().decl_fun(name, n, params, range); | ||||
|             SASSERT(! m_defs.contains(d->get_decl())); | ||||
|             m_defs.insert(d->get_decl(), d); | ||||
|             return promise_def(&u(), d); | ||||
|         } | ||||
|          | ||||
|         void plugin::set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs) { | ||||
|             u().set_definition(d, n_vars, vars, rhs); | ||||
|             for (case_def & c : d.get_def()->get_cases()) { | ||||
|                 m_case_defs.insert(c.get_decl(), &c); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         bool plugin::has_defs() const { | ||||
|             return !m_case_defs.empty();             | ||||
|         } | ||||
| 
 | ||||
|         def* plugin::mk_def(symbol const& name, unsigned n, sort ** params, sort * range, | ||||
|                             unsigned n_vars, var ** vars, expr * rhs) { | ||||
|             promise_def d = mk_def(name, n, params, range); | ||||
|             SASSERT(! m_defs.contains(d.get_def()->get_decl())); | ||||
|             set_definition(d, n_vars, vars, rhs); | ||||
|             return d.get_def(); | ||||
|         } | ||||
| 
 | ||||
|         // generic declaration of symbols
 | ||||
|         func_decl * plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,  | ||||
|                                          unsigned arity, sort * const * domain, sort * range) | ||||
|         { | ||||
|             UNREACHABLE(); | ||||
|             return nullptr;             | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										249
									
								
								src/ast/recfun_decl_plugin.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/ast/recfun_decl_plugin.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,249 @@ | |||
| /*++
 | ||||
| Copyright (c) 2017 Microsoft Corporation, Simon Cruanes | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     recfun_decl_plugin.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Declaration and definition of (potentially recursive) functions | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Simon Cruanes 2017-11 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast/ast.h" | ||||
| #include "ast/rewriter/th_rewriter.h" | ||||
| #include "util/obj_hashtable.h" | ||||
| 
 | ||||
| namespace recfun { | ||||
|     class case_def;    //<! one possible control path of a function
 | ||||
|     class case_pred;   //<! a predicate guarding a given control flow path of a function
 | ||||
|     class util;        //<! util for other modules
 | ||||
|     class def;         //!< definition of a (recursive) function
 | ||||
|     class promise_def; //!< definition to be complete
 | ||||
| 
 | ||||
|     enum op_kind { | ||||
|         OP_FUN_DEFINED, // defined function with one or more cases, possibly recursive
 | ||||
|         OP_FUN_CASE_PRED, // predicate guarding a given control flow path
 | ||||
|         OP_DEPTH_LIMIT, // predicate enforcing some depth limit
 | ||||
|     }; | ||||
| 
 | ||||
|     /*! A predicate `p(t1...tn)`, that, if true, means `f(t1...tn)` is following
 | ||||
|         a given path of its control flow and can be unrolled. | ||||
| 
 | ||||
|         For example, `fact n := if n<2 then 1 else n * fact(n-1)` would have two cases, | ||||
|         and therefore two case predicates `C_fact_0` and `C_fact_1`, where | ||||
|         `C_fact_0(t)=true` means `t<2` (first path) and `C_fact_1(t)=true` means `not (t<2)` (second path). | ||||
|     */ | ||||
| 
 | ||||
|     typedef var_ref_vector vars; | ||||
| 
 | ||||
|     class case_def { | ||||
|         friend class def; | ||||
|         func_decl_ref       m_pred; //<! predicate used for this case
 | ||||
|         expr_ref_vector     m_guards; //<! conjunction that is equivalent to this case
 | ||||
|         expr_ref            m_rhs; //<! if guard is true, `f(t1…tn) = rhs` holds
 | ||||
|         def *               m_def; //<! definition this is a part of
 | ||||
|         bool                m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
 | ||||
| 
 | ||||
|         case_def(ast_manager & m, | ||||
|                  family_id fid, | ||||
|                  def * d, | ||||
|                  std::string & name, | ||||
|                  unsigned case_index, | ||||
|                  sort_ref_vector const & arg_sorts, | ||||
|                  expr_ref_vector const& guards, | ||||
|                  expr* rhs); | ||||
| 
 | ||||
|         void add_guard(expr_ref && e) { m_guards.push_back(e); } | ||||
|     public: | ||||
|         func_decl* get_decl() const { return m_pred; } | ||||
| 
 | ||||
|         app_ref apply_case_predicate(ptr_vector<expr> const & args) const { | ||||
|             ast_manager& m = m_pred.get_manager(); | ||||
|             return app_ref(m.mk_app(m_pred, args.size(), args.c_ptr()), m); | ||||
|         } | ||||
| 
 | ||||
|         def * get_def() const { return m_def; } | ||||
|         expr_ref_vector const & get_guards() const { return m_guards; } | ||||
|         expr * get_guards_c_ptr() const { return *m_guards.c_ptr(); } | ||||
|         expr * get_guard(unsigned i) const { return m_guards[i]; } | ||||
|         expr * get_rhs() const { return m_rhs; } | ||||
|         unsigned num_guards() const { return m_guards.size(); } | ||||
|         bool is_immediate() const { return m_immediate; }; | ||||
|         void set_is_immediate(bool b) { m_immediate = b; } | ||||
|     }; | ||||
| 
 | ||||
|     // closure for computing whether a `rhs` expression is immediate
 | ||||
|     struct is_immediate_pred { | ||||
|         virtual bool operator()(expr * rhs) = 0; | ||||
|     }; | ||||
| 
 | ||||
|     class def { | ||||
|         friend class util; | ||||
|         friend class promise_def; | ||||
|         typedef vector<case_def> cases; | ||||
| 
 | ||||
|         ast_manager &       m; | ||||
|         symbol              m_name; //<! name of function
 | ||||
|         sort_ref_vector     m_domain; //<! type of arguments
 | ||||
|         sort_ref            m_range; //<! return type
 | ||||
|         vars                m_vars; //<! variables of the function
 | ||||
|         cases               m_cases; //!< possible cases
 | ||||
|         func_decl_ref       m_decl; //!< generic declaration
 | ||||
|         expr_ref            m_rhs;  //!< definition
 | ||||
|         family_id           m_fid; | ||||
| 
 | ||||
|         def(ast_manager &m, family_id fid, symbol const & s, unsigned arity, sort *const * domain, sort* range); | ||||
| 
 | ||||
|         // compute cases for a function, given its RHS (possibly containing `ite`).
 | ||||
|         void compute_cases(is_immediate_pred &, th_rewriter & th_rw, | ||||
|                            unsigned n_vars, var *const * vars, expr* rhs); | ||||
|         void add_case(std::string & name, unsigned case_index, expr_ref_vector const& conditions, expr* rhs, bool is_imm = false); | ||||
|         bool contains_ite(expr* e); // expression contains a test?
 | ||||
|     public: | ||||
|         symbol const & get_name() const { return m_name; } | ||||
|         vars const & get_vars() const { return m_vars; } | ||||
|         cases & get_cases() { return m_cases; } | ||||
|         unsigned get_arity() const { return m_domain.size(); } | ||||
|         sort_ref_vector const & get_domain() const { return m_domain; } | ||||
|         sort_ref const & get_range() const { return m_range; } | ||||
|         func_decl * get_decl() const { return m_decl.get(); } | ||||
|         expr * get_rhs() const { return m_rhs; } | ||||
| 
 | ||||
|         bool is_fun_macro() const { return m_cases.size() == 1; } | ||||
|         bool is_fun_defined() const { return !is_fun_macro(); } | ||||
| 
 | ||||
|     }; | ||||
|      | ||||
|     // definition to be complete (missing RHS)
 | ||||
|     class promise_def { | ||||
|         friend class util; | ||||
|         util * u; | ||||
|         def * d; | ||||
|         void set_definition(unsigned n_vars, var * const * vars, expr * rhs); // call only once
 | ||||
|     public: | ||||
|         promise_def(util * u, def * d) : u(u), d(d) {} | ||||
|         promise_def(promise_def const & from) : u(from.u), d(from.d) {} | ||||
|         def * get_def() const { return d; } | ||||
|     }; | ||||
| 
 | ||||
|     namespace decl { | ||||
| 
 | ||||
|         class plugin : public decl_plugin { | ||||
|             typedef obj_map<func_decl, def*> def_map; | ||||
|             typedef obj_map<func_decl, case_def*> case_def_map; | ||||
| 
 | ||||
|             mutable scoped_ptr<util> m_util; | ||||
|             def_map                  m_defs;       // function->def
 | ||||
|             case_def_map             m_case_defs;  // case_pred->def
 | ||||
|              | ||||
|             ast_manager & m() { return *m_manager; } | ||||
|         public: | ||||
|             plugin(); | ||||
|             ~plugin() override; | ||||
|             void finalize() override; | ||||
| 
 | ||||
|             util & u() const; // build or return util
 | ||||
| 
 | ||||
|             bool is_fully_interp(sort * s) const override { return false; } // might depend on unin sorts
 | ||||
|          | ||||
|             decl_plugin * mk_fresh() override { return alloc(plugin); } | ||||
|          | ||||
|             sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override {  | ||||
|                 UNREACHABLE(); return nullptr;  | ||||
|             } | ||||
|          | ||||
|             func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,  | ||||
|                                      unsigned arity, sort * const * domain, sort * range) override; | ||||
|              | ||||
|             promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range); | ||||
|              | ||||
|             void set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs); | ||||
|              | ||||
|             def* mk_def(symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs); | ||||
| 
 | ||||
|             bool has_def(func_decl* f) const { return m_defs.contains(f); } | ||||
|             bool has_defs() const; | ||||
|             def const& get_def(func_decl* f) const { return *(m_defs[f]); } | ||||
|             promise_def get_promise_def(func_decl* f) const { return promise_def(&u(), m_defs[f]); } | ||||
|             def& get_def(func_decl* f) { return *(m_defs[f]); } | ||||
|             bool has_case_def(func_decl* f) const { return m_case_defs.contains(f); } | ||||
|             case_def& get_case_def(func_decl* f) { SASSERT(has_case_def(f)); return *(m_case_defs[f]); } | ||||
| 
 | ||||
|             func_decl_ref_vector get_rec_funs() { | ||||
|                 func_decl_ref_vector result(m()); | ||||
|                 for (auto& kv : m_defs) result.push_back(kv.m_key); | ||||
|                 return result; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     // Varus utils for recursive functions
 | ||||
|     class util { | ||||
|         friend class decl::plugin; | ||||
|          | ||||
|         ast_manager &           m_manager; | ||||
|         family_id               m_fid; | ||||
|         th_rewriter             m_th_rw; | ||||
|         decl::plugin *          m_plugin; | ||||
| 
 | ||||
|         bool compute_is_immediate(expr * rhs); | ||||
|         void set_definition(promise_def & d, unsigned n_vars, var * const * vars, expr * rhs); | ||||
| 
 | ||||
|     public: | ||||
|         util(ast_manager &m); | ||||
|         ~util(); | ||||
| 
 | ||||
|         ast_manager & m() { return m_manager; } | ||||
|         th_rewriter & get_th_rewriter() { return m_th_rw; } | ||||
|         decl::plugin& get_plugin() { return *m_plugin; } | ||||
| 
 | ||||
|         bool is_case_pred(expr * e) const { return is_app_of(e, m_fid, OP_FUN_CASE_PRED); } | ||||
|         bool is_defined(expr * e) const { return is_app_of(e, m_fid, OP_FUN_DEFINED); } | ||||
|         bool is_defined(func_decl* f) const { return is_decl_of(f, m_fid, OP_FUN_DEFINED); } | ||||
|         bool is_depth_limit(expr * e) const { return is_app_of(e, m_fid, OP_DEPTH_LIMIT); } | ||||
|         bool owns_app(app * e) const { return e->get_family_id() == m_fid; } | ||||
| 
 | ||||
|         //<! don't use native theory if recursive function declarations are not populated with defs
 | ||||
|         bool has_defs() const { return m_plugin->has_defs(); } | ||||
| 
 | ||||
|         //<! add a function declaration
 | ||||
|         def * decl_fun(symbol const & s, unsigned n_args, sort *const * args, sort * range); | ||||
| 
 | ||||
| 
 | ||||
|         def& get_def(func_decl* f) { | ||||
|             SASSERT(m_plugin->has_def(f)); | ||||
|             return m_plugin->get_def(f); | ||||
|         } | ||||
| 
 | ||||
|         case_def& get_case_def(expr* e) { | ||||
|             SASSERT(is_case_pred(e)); | ||||
|             return m_plugin->get_case_def(to_app(e)->get_decl()); | ||||
|         } | ||||
| 
 | ||||
|         app* mk_fun_defined(def const & d, unsigned n_args, expr * const * args) { | ||||
|             return m().mk_app(d.get_decl(), n_args, args); | ||||
|         } | ||||
| 
 | ||||
|         app* mk_fun_defined(def const & d, ptr_vector<expr> const & args) { | ||||
|             return mk_fun_defined(d, args.size(), args.c_ptr()); | ||||
|         } | ||||
| 
 | ||||
|         func_decl_ref_vector get_rec_funs() { | ||||
|             return m_plugin->get_rec_funs(); | ||||
|         } | ||||
| 
 | ||||
|         app_ref mk_depth_limit_pred(unsigned d); | ||||
| 
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  | @ -22,6 +22,7 @@ Revision History: | |||
| #include "ast/array_decl_plugin.h" | ||||
| #include "ast/bv_decl_plugin.h" | ||||
| #include "ast/datatype_decl_plugin.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| #include "ast/dl_decl_plugin.h" | ||||
| #include "ast/seq_decl_plugin.h" | ||||
| #include "ast/pb_decl_plugin.h" | ||||
|  | @ -40,6 +41,9 @@ void reg_decl_plugins(ast_manager & m) { | |||
|     if (!m.get_plugin(m.mk_family_id(symbol("datatype")))) { | ||||
|         m.register_plugin(symbol("datatype"), alloc(datatype_decl_plugin));     | ||||
|     } | ||||
|     if (!m.get_plugin(m.mk_family_id(symbol("recfun")))) { | ||||
|         m.register_plugin(symbol("recfun"), alloc(recfun::decl::plugin));     | ||||
|     } | ||||
|     if (!m.get_plugin(m.mk_family_id(symbol("datalog_relation")))) { | ||||
|         m.register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin)); | ||||
|     } | ||||
|  |  | |||
|  | @ -279,8 +279,9 @@ protected: | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) { | ||||
|         return m_cfg.get_macro(f, def, q, def_pr); | ||||
|     bool get_macro(func_decl * f, expr * & def, proof * & def_pr) { | ||||
|         quantifier* q = nullptr; | ||||
|         return m_cfg.get_macro(f, def, q, def_pr);  | ||||
|     } | ||||
| 
 | ||||
|     void push_frame(expr * t, bool mcache, unsigned max_depth) { | ||||
|  |  | |||
|  | @ -212,6 +212,7 @@ bool rewriter_tpl<Config>::constant_fold(app * t, frame & fr) { | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template<typename Config> | ||||
| template<bool ProofGen> | ||||
| void rewriter_tpl<Config>::process_app(app * t, frame & fr) { | ||||
|  | @ -338,15 +339,12 @@ void rewriter_tpl<Config>::process_app(app * t, frame & fr) { | |||
|         // TODO: add rewrite rules support
 | ||||
|         expr * def = nullptr; | ||||
|         proof * def_pr = nullptr; | ||||
|         quantifier * def_q = nullptr; | ||||
|         // When get_macro succeeds, then
 | ||||
|         // we know that:
 | ||||
|         // forall X. f(X) = def[X]
 | ||||
|         // and def_pr is a proof for this quantifier.
 | ||||
|         //
 | ||||
|         // Remark: def_q is only used for proof generation.
 | ||||
|         // It is the quantifier forall X. f(X) = def[X]
 | ||||
|         if (get_macro(f, def, def_q, def_pr)) { | ||||
|         if (get_macro(f, def, def_pr)) { | ||||
|             SASSERT(!f->is_associative() || !flat_assoc(f)); | ||||
|             SASSERT(new_num_args == t->get_num_args()); | ||||
|             SASSERT(m().get_sort(def) == m().get_sort(t)); | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ Notes: | |||
| #include "ast/seq_decl_plugin.h" | ||||
| #include "ast/pb_decl_plugin.h" | ||||
| #include "ast/fpa_decl_plugin.h" | ||||
| #include "ast/csp_decl_plugin.h" | ||||
| #include "ast/ast_pp.h" | ||||
| #include "ast/rewriter/var_subst.h" | ||||
| #include "ast/pp.h" | ||||
|  | @ -474,7 +475,6 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): | |||
|     m_manager(m), | ||||
|     m_own_manager(m == nullptr), | ||||
|     m_manager_initialized(false), | ||||
|     m_rec_fun_declared(false), | ||||
|     m_pmanager(nullptr), | ||||
|     m_sexpr_manager(nullptr), | ||||
|     m_regular("stdout", std::cout), | ||||
|  | @ -679,6 +679,8 @@ bool cmd_context::logic_has_datatype() const { | |||
|     return !has_logic() || smt_logics::logic_has_datatype(m_logic); | ||||
| } | ||||
| 
 | ||||
| bool cmd_context::logic_has_recfun() const { return true; } | ||||
| 
 | ||||
| void cmd_context::init_manager_core(bool new_manager) { | ||||
|     SASSERT(m_manager != 0); | ||||
|     SASSERT(m_pmanager != 0); | ||||
|  | @ -691,10 +693,12 @@ void cmd_context::init_manager_core(bool new_manager) { | |||
|         register_plugin(symbol("bv"),       alloc(bv_decl_plugin), logic_has_bv()); | ||||
|         register_plugin(symbol("array"),    alloc(array_decl_plugin), logic_has_array()); | ||||
|         register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype()); | ||||
|         register_plugin(symbol("recfun"),   alloc(recfun::decl::plugin), logic_has_recfun()); | ||||
|         register_plugin(symbol("seq"),      alloc(seq_decl_plugin), logic_has_seq()); | ||||
|         register_plugin(symbol("pb"),       alloc(pb_decl_plugin), logic_has_pb()); | ||||
|         register_plugin(symbol("fpa"),      alloc(fpa_decl_plugin), logic_has_fpa()); | ||||
|         register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic()); | ||||
|         register_plugin(symbol("csp"),      alloc(csp_decl_plugin), smt_logics::logic_is_csp(m_logic)); | ||||
|     } | ||||
|     else { | ||||
|         // the manager was created by an external module
 | ||||
|  | @ -706,9 +710,11 @@ void cmd_context::init_manager_core(bool new_manager) { | |||
|         load_plugin(symbol("bv"),       logic_has_bv(), fids); | ||||
|         load_plugin(symbol("array"),    logic_has_array(), fids); | ||||
|         load_plugin(symbol("datatype"), logic_has_datatype(), fids); | ||||
|         load_plugin(symbol("recfun"),   logic_has_recfun(), fids); | ||||
|         load_plugin(symbol("seq"),      logic_has_seq(), fids); | ||||
|         load_plugin(symbol("fpa"),      logic_has_fpa(), fids); | ||||
|         load_plugin(symbol("pb"),       logic_has_pb(), fids); | ||||
|         load_plugin(symbol("csp"),      smt_logics::logic_is_csp(m_logic), fids); | ||||
|         for (family_id fid : fids) { | ||||
|             decl_plugin * p = m_manager->get_plugin(fid); | ||||
|             if (p) { | ||||
|  | @ -790,9 +796,11 @@ void cmd_context::insert(symbol const & s, func_decl * f) { | |||
|     if (contains_macro(s, f)) { | ||||
|         throw cmd_exception("invalid declaration, named expression already defined with this name ", s); | ||||
|     } | ||||
| #if 0 | ||||
|     if (m_builtin_decls.contains(s)) { | ||||
|         throw cmd_exception("invalid declaration, builtin symbol ", s); | ||||
|     } | ||||
| #endif | ||||
|     dictionary<func_decls>::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); | ||||
|     func_decls & fs = e->get_data().m_value; | ||||
|     if (!fs.insert(m(), f)) { | ||||
|  | @ -827,9 +835,11 @@ void cmd_context::insert(symbol const & s, psort_decl * p) { | |||
| 
 | ||||
| void cmd_context::insert(symbol const & s, unsigned arity, sort *const* domain, expr * t) { | ||||
|     expr_ref _t(t, m()); | ||||
| #if 0 | ||||
|     if (m_builtin_decls.contains(s)) { | ||||
|         throw cmd_exception("invalid macro/named expression, builtin symbol ", s); | ||||
|     } | ||||
| #endif | ||||
|     if (contains_macro(s, arity, domain)) { | ||||
|         throw cmd_exception("named expression already defined"); | ||||
|     } | ||||
|  | @ -888,7 +898,20 @@ void cmd_context::model_del(func_decl* f) { | |||
|     m_mc0->hide(f); | ||||
| } | ||||
| 
 | ||||
| void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e) { | ||||
| 
 | ||||
| recfun::decl::plugin& cmd_context::get_recfun_plugin() { | ||||
|     recfun::util u(get_ast_manager()); | ||||
|     return u.get_plugin(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| recfun::promise_def cmd_context::decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range) {         | ||||
|     SASSERT(logic_has_recfun()); | ||||
|     return get_recfun_plugin().mk_def(name, arity, domain, range); | ||||
| } | ||||
| 
 | ||||
| // insert a recursive function as a regular quantified axiom
 | ||||
| void cmd_context::insert_rec_fun_as_axiom(func_decl *f, expr_ref_vector const& binding, svector<symbol> const &ids, expr* e) { | ||||
|     expr_ref eq(m()); | ||||
|     app_ref lhs(m()); | ||||
|     lhs = m().mk_app(f, binding.size(), binding.c_ptr()); | ||||
|  | @ -907,19 +930,42 @@ void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, s | |||
|         eq  = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq, 0, m().rec_fun_qid(), symbol::null, 2, pats); | ||||
|     } | ||||
| 
 | ||||
|     //
 | ||||
|     // disable warning given the current way they are used
 | ||||
|     // (Z3 will here silently assume and not check the definitions to be well founded,
 | ||||
|     // and please use HSF for everything else).
 | ||||
|     //
 | ||||
|     if (false && !ids.empty() && !m_rec_fun_declared) { | ||||
|         warning_msg("recursive function definitions are assumed well-founded"); | ||||
|         m_rec_fun_declared = true; | ||||
|     } | ||||
|     assert_expr(eq); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* rhs) { | ||||
| 
 | ||||
|     if (gparams::get_value("smt.recfun.native") != "true") { | ||||
|         // just use an axiom
 | ||||
|         insert_rec_fun_as_axiom(f, binding, ids, rhs); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     TRACE("recfun", tout<< "define recfun " << f->get_name()  << " = " << mk_pp(rhs, m()) << "\n";); | ||||
| 
 | ||||
|     recfun::decl::plugin& p = get_recfun_plugin(); | ||||
| 
 | ||||
|     var_ref_vector vars(m()); | ||||
|     for (expr* b : binding) { | ||||
|         SASSERT(is_var(b)); | ||||
|         vars.push_back(to_var(b)); | ||||
|     } | ||||
|      | ||||
|     recfun::promise_def d = p.get_promise_def(f); | ||||
|     p.set_definition(d, vars.size(), vars.c_ptr(), rhs); | ||||
| } | ||||
| 
 | ||||
| func_decl * cmd_context::find_func_decl(symbol const & s) const { | ||||
|     if (contains_macro(s)) { | ||||
|         throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); | ||||
|     } | ||||
|     func_decls fs; | ||||
|     if (m_func_decls.find(s, fs)) { | ||||
|         if (fs.more_than_one()) | ||||
|             throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate (<symbol> (<sort>*) <sort>) ", s); | ||||
|         return fs.first(); | ||||
|     } | ||||
|     builtin_decl d; | ||||
|     if (m_builtin_decls.find(s, d)) { | ||||
|         try { | ||||
|  | @ -933,15 +979,6 @@ func_decl * cmd_context::find_func_decl(symbol const & s) const { | |||
|         } | ||||
|         throw cmd_exception("invalid function declaration reference, must provide signature for builtin symbol ", s); | ||||
|     } | ||||
|     if (contains_macro(s)) { | ||||
|         throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); | ||||
|     } | ||||
|     func_decls fs; | ||||
|     if (m_func_decls.find(s, fs)) { | ||||
|         if (fs.more_than_one()) | ||||
|             throw cmd_exception("ambiguous function declaration reference, provide full signature to disumbiguate (<symbol> (<sort>*) <sort>) ", s); | ||||
|         return fs.first(); | ||||
|     } | ||||
|     throw cmd_exception("invalid function declaration reference, unknown function ", s); | ||||
|     return nullptr; | ||||
| } | ||||
|  | @ -966,6 +1003,18 @@ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family | |||
| 
 | ||||
| func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, | ||||
|                                         unsigned arity, sort * const * domain, sort * range) const { | ||||
| 
 | ||||
|     if (domain && contains_macro(s, arity, domain)) | ||||
|         throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); | ||||
| 
 | ||||
|     func_decl * f = nullptr; | ||||
|     func_decls fs; | ||||
|     if (num_indices == 0 && m_func_decls.find(s, fs)) { | ||||
|         f = fs.find(arity, domain, range); | ||||
|     } | ||||
|     if (f) { | ||||
|         return f; | ||||
|     } | ||||
|     builtin_decl d; | ||||
|     if (domain && m_builtin_decls.find(s, d)) { | ||||
|         family_id fid = d.m_fid; | ||||
|  | @ -990,21 +1039,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, | |||
|             throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); | ||||
|         return f; | ||||
|     } | ||||
| 
 | ||||
|     if (domain && contains_macro(s, arity, domain)) | ||||
|         throw cmd_exception("invalid function declaration reference, named expressions (aka macros) cannot be referenced ", s); | ||||
| 
 | ||||
|     if (num_indices > 0) | ||||
|         throw cmd_exception("invalid indexed function declaration reference, unknown builtin function ", s); | ||||
| 
 | ||||
|     func_decl * f = nullptr; | ||||
|     func_decls fs; | ||||
|     if (m_func_decls.find(s, fs)) { | ||||
|         f = fs.find(arity, domain, range); | ||||
|     } | ||||
|     if (f == nullptr) | ||||
|         throw cmd_exception("invalid function declaration reference, unknown function ", s); | ||||
|     return f; | ||||
|     throw cmd_exception("invalid function declaration reference, unknown function ", s); | ||||
| } | ||||
| 
 | ||||
| psort_decl * cmd_context::find_psort_decl(symbol const & s) const { | ||||
|  | @ -1041,29 +1076,6 @@ void cmd_context::mk_const(symbol const & s, expr_ref & result) const { | |||
| 
 | ||||
| void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, | ||||
|                          expr_ref & result) const { | ||||
|     builtin_decl d; | ||||
|     if (m_builtin_decls.find(s, d)) { | ||||
|         family_id fid = d.m_fid; | ||||
|         decl_kind k   = d.m_decl; | ||||
|         // Hack: if d.m_next != 0, we use the sort of args[0] (if available) to decide which plugin we use.
 | ||||
|         if (d.m_decl != 0 && num_args > 0) { | ||||
|             builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id()); | ||||
|             fid = d2.m_fid; | ||||
|             k   = d2.m_decl; | ||||
|         } | ||||
|         if (num_indices == 0) { | ||||
|             result = m().mk_app(fid, k, 0, nullptr, num_args, args, range); | ||||
|         } | ||||
|         else { | ||||
|             result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); | ||||
|         } | ||||
|         if (result.get() == nullptr) | ||||
|             throw cmd_exception("invalid builtin application ", s); | ||||
|         CHECK_SORT(result.get()); | ||||
|         return; | ||||
|     } | ||||
|     if (num_indices > 0) | ||||
|         throw cmd_exception("invalid use of indexed identifier, unknown builtin function ", s); | ||||
|     expr* _t; | ||||
|     if (macros_find(s, num_args, args, _t)) { | ||||
|         TRACE("macro_bug", tout << "well_sorted_check_enabled(): " << well_sorted_check_enabled() << "\n"; | ||||
|  | @ -1079,6 +1091,31 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg | |||
| 
 | ||||
|     func_decls fs; | ||||
|     if (!m_func_decls.find(s, fs)) { | ||||
| 
 | ||||
|         builtin_decl d; | ||||
|         if (m_builtin_decls.find(s, d)) { | ||||
|             family_id fid = d.m_fid; | ||||
|             decl_kind k   = d.m_decl; | ||||
|             // Hack: if d.m_next != 0, we use the sort of args[0] (if available) to decide which plugin we use.
 | ||||
|             if (d.m_decl != 0 && num_args > 0) { | ||||
|                 builtin_decl const & d2 = peek_builtin_decl(d, m().get_sort(args[0])->get_family_id()); | ||||
|                 fid = d2.m_fid; | ||||
|                 k   = d2.m_decl; | ||||
|             } | ||||
|             if (num_indices == 0) { | ||||
|                 result = m().mk_app(fid, k, 0, nullptr, num_args, args, range); | ||||
|             } | ||||
|             else { | ||||
|                 result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); | ||||
|         } | ||||
|             if (result.get() == nullptr) | ||||
|                 throw cmd_exception("invalid builtin application ", s); | ||||
|             CHECK_SORT(result.get()); | ||||
|             return; | ||||
|         } | ||||
|         if (num_indices > 0) | ||||
|             throw cmd_exception("invalid use of indexed identifier, unknown builtin function ", s); | ||||
|          | ||||
|         if (num_args == 0) { | ||||
|             throw cmd_exception("unknown constant ", s); | ||||
|         } | ||||
|  | @ -1088,14 +1125,17 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg | |||
| 
 | ||||
|     if (num_args == 0 && range == nullptr) { | ||||
|         if (fs.more_than_one()) | ||||
|             throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as <symbol> <sort>) to disumbiguate ", s); | ||||
|             throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as <symbol> <sort>) to disambiguate ", s); | ||||
|         func_decl * f = fs.first(); | ||||
|         if (f == nullptr) { | ||||
|             throw cmd_exception("unknown constant ", s); | ||||
|         } | ||||
|         if (f->get_arity() != 0) | ||||
|             throw cmd_exception("invalid function application, missing arguments ", s); | ||||
|         result = m().mk_const(f); | ||||
|         if (f->get_arity() != 0) { | ||||
|             result = array_util(m()).mk_as_array(f); | ||||
|         } | ||||
|         else { | ||||
|             result = m().mk_const(f); | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
|         func_decl * f = fs.find(m(), num_args, args, range); | ||||
|  | @ -2000,7 +2040,7 @@ void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr | |||
|     if (logic != symbol::null) | ||||
|         out << "(set-logic " << logic << ")" << std::endl; | ||||
|     // collect uninterpreted function declarations
 | ||||
|     decl_collector decls(m(), false); | ||||
|     decl_collector decls(m()); | ||||
|     for (unsigned i = 0; i < num; i++) { | ||||
|         decls.visit(assertions[i]); | ||||
|     } | ||||
|  | @ -2031,8 +2071,8 @@ void cmd_context::slow_progress_sample() { | |||
|     svector<symbol> labels; | ||||
|     m_solver->get_labels(labels); | ||||
|     regular_stream() << "(labels"; | ||||
|     for (unsigned i = 0; i < labels.size(); i++) { | ||||
|         regular_stream() << " " << labels[i]; | ||||
|     for (symbol const& s : labels) { | ||||
|         regular_stream() << " " << s; | ||||
|     } | ||||
|     regular_stream() << "))" << std::endl; | ||||
| } | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ Notes: | |||
| #include "ast/ast.h" | ||||
| #include "ast/ast_printer.h" | ||||
| #include "ast/datatype_decl_plugin.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| #include "tactic/generic_model_converter.h" | ||||
| #include "solver/solver.h" | ||||
| #include "solver/progress_callback.h" | ||||
|  | @ -199,7 +200,6 @@ protected: | |||
|     ast_manager *                m_manager; | ||||
|     bool                         m_own_manager; | ||||
|     bool                         m_manager_initialized; | ||||
|     bool                         m_rec_fun_declared; | ||||
|     pdecl_manager *              m_pmanager; | ||||
|     sexpr_manager *              m_sexpr_manager; | ||||
|     check_logic                  m_check_logic; | ||||
|  | @ -291,6 +291,7 @@ protected: | |||
|     bool logic_has_array() const; | ||||
|     bool logic_has_datatype() const; | ||||
|     bool logic_has_fpa() const; | ||||
|     bool logic_has_recfun() const; | ||||
| 
 | ||||
|     void print_unsupported_msg() { regular_stream() << "unsupported" << std::endl; } | ||||
|     void print_unsupported_info(symbol const& s, int line, int pos) { if (s != symbol::null) diagnostic_stream() << "; " << s << " line: " << line << " position: " << pos << std::endl;} | ||||
|  | @ -306,6 +307,7 @@ protected: | |||
|     void erase_macro(symbol const& s); | ||||
|     bool macros_find(symbol const& s, unsigned n, expr*const* args, expr*& t) const; | ||||
| 
 | ||||
|     recfun::decl::plugin& get_recfun_plugin(); | ||||
| 
 | ||||
| public: | ||||
|     cmd_context(bool main_ctx = true, ast_manager * m = nullptr, symbol const & l = symbol::null); | ||||
|  | @ -384,12 +386,14 @@ public: | |||
|     void insert(probe_info * p) { tactic_manager::insert(p); } | ||||
|     void insert_user_tactic(symbol const & s, sexpr * d); | ||||
|     void insert_aux_pdecl(pdecl * p); | ||||
|     void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e); | ||||
|     void model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t); | ||||
|     void model_del(func_decl* f); | ||||
|     void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e); | ||||
|     void insert_rec_fun_as_axiom(func_decl* f, expr_ref_vector const& binding, svector<symbol> const& ids, expr* e); | ||||
|     func_decl * find_func_decl(symbol const & s) const; | ||||
|     func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, | ||||
|                                unsigned arity, sort * const * domain, sort * range) const; | ||||
|     recfun::promise_def decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range); | ||||
|     psort_decl * find_psort_decl(symbol const & s) const; | ||||
|     cmd * find_cmd(symbol const & s) const; | ||||
|     sexpr * find_user_tactic(symbol const & s) const; | ||||
|  |  | |||
|  | @ -91,6 +91,11 @@ UNARY_CMD(pp_shared_cmd, "dbg-pp-shared", "<term>", "display shared subterms of | |||
|     ctx.regular_stream() << ")" << std::endl; | ||||
| }); | ||||
| 
 | ||||
| UNARY_CMD(assert_not_cmd, "assert-not", "<term>", "assert negation", CPK_EXPR, expr *, { | ||||
|     expr_ref ne(ctx.m().mk_not(arg), ctx.m()); | ||||
|     ctx.assert_expr(ne); | ||||
| }); | ||||
| 
 | ||||
| UNARY_CMD(num_shared_cmd, "dbg-num-shared", "<term>", "return the number of shared subterms", CPK_EXPR, expr *, { | ||||
|     shared_occs s(ctx.m()); | ||||
|     s(arg); | ||||
|  | @ -537,6 +542,7 @@ void install_dbg_cmds(cmd_context & ctx) { | |||
|     ctx.insert(alloc(shift_vars_cmd)); | ||||
|     ctx.insert(alloc(pp_shared_cmd)); | ||||
|     ctx.insert(alloc(num_shared_cmd)); | ||||
|     ctx.insert(alloc(assert_not_cmd)); | ||||
|     ctx.insert(alloc(size_cmd)); | ||||
|     ctx.insert(alloc(subst_cmd)); | ||||
|     ctx.insert(alloc(bool_rewriter_cmd)); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ Revision History: | |||
| #include "model/model_smt2_pp.h" | ||||
| #include "ast/ast_smt2_pp.h" | ||||
| #include "ast/func_decl_dependencies.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| #include "ast/pp.h" | ||||
| using namespace format_ns; | ||||
| 
 | ||||
|  | @ -60,11 +61,9 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod | |||
|         ctx.display(buffer, s, 13); | ||||
|         buffer << ":\n"; | ||||
|         pp_indent(buffer, TAB_SZ); | ||||
|         ptr_vector<expr>::const_iterator it  = u.begin(); | ||||
|         ptr_vector<expr>::const_iterator end = u.end(); | ||||
|         for (; it != end; ++it) { | ||||
|             SASSERT(is_app(*it)); | ||||
|             app * c = to_app(*it); | ||||
|         for (expr* e : u) { | ||||
|             SASSERT(is_app(e)); | ||||
|             app * c = to_app(e); | ||||
|             pp_symbol(buffer, c->get_decl()->get_name()); | ||||
|             buffer << " "; | ||||
|         } | ||||
|  | @ -87,9 +86,8 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod | |||
|         out << "\n"; | ||||
|         pp_indent(out, indent); | ||||
|         out << ";; definitions for universe elements:\n"; | ||||
|         it  = u.begin(); | ||||
|         for (; it != end; ++it) { | ||||
|             app * c = to_app(*it); | ||||
|         for (expr * e : u) { | ||||
|             app * c = to_app(e); | ||||
|             pp_indent(out, indent); | ||||
|             out << "(declare-fun "; | ||||
|             unsigned len  = pp_symbol(out, c->get_decl()->get_name()); | ||||
|  | @ -101,9 +99,8 @@ static void pp_uninterp_sorts(std::ostream & out, ast_printer_context & ctx, mod | |||
|         out << ";; cardinality constraint:\n"; | ||||
|         f_conds.reset(); | ||||
|         format * var = mk_string(m, "x"); | ||||
|         it  = u.begin(); | ||||
|         for (; it != end; ++it) { | ||||
|             app * c = to_app(*it); | ||||
|         for (expr* e : u) { | ||||
|             app * c = to_app(e); | ||||
|             symbol csym = c->get_decl()->get_name(); | ||||
|             std::string cname; | ||||
|             if (is_smt2_quoted_symbol(csym)) | ||||
|  | @ -170,10 +167,7 @@ void sort_fun_decls(ast_manager & m, model_core const & md, ptr_buffer<func_decl | |||
|                 func_decl_set deps; | ||||
|                 bool all_visited = true; | ||||
|                 collect_func_decls(m, curr_i->get_else(), deps); | ||||
|                 func_decl_set::iterator it2  = deps.begin(); | ||||
|                 func_decl_set::iterator end2 = deps.end(); | ||||
|                 for (; it2 != end2; ++it2) { | ||||
|                     func_decl * d = *it2; | ||||
|                 for (func_decl * d : deps) { | ||||
|                     if (d->get_arity() > 0 && md.has_interpretation(d) && !visited.contains(d)) { | ||||
|                         todo.push_back(d); | ||||
|                         visited.insert(d); | ||||
|  | @ -189,8 +183,10 @@ void sort_fun_decls(ast_manager & m, model_core const & md, ptr_buffer<func_decl | |||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core const & md, unsigned indent) { | ||||
|     ast_manager & m = ctx.get_ast_manager(); | ||||
|     recfun::util recfun_util(m); | ||||
|     sbuffer<symbol>    var_names; | ||||
|     ptr_buffer<format> f_var_names; | ||||
|     ptr_buffer<format> f_arg_decls; | ||||
|  | @ -200,6 +196,9 @@ static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core co | |||
|     sort_fun_decls(m, md, func_decls); | ||||
|     for (unsigned i = 0; i < func_decls.size(); i++) { | ||||
|         func_decl * f     = func_decls[i];  | ||||
|         if (recfun_util.is_defined(f)) { | ||||
|             continue; | ||||
|         } | ||||
|         func_interp * f_i = md.get_func_interp(f); | ||||
|         SASSERT(f->get_arity() == f_i->get_arity()); | ||||
|         format_ref body(fm(m)); | ||||
|  |  | |||
|  | @ -114,6 +114,7 @@ namespace smt2 { | |||
|         symbol               m_define_fun_rec; | ||||
|         symbol               m_define_funs_rec; | ||||
|         symbol               m_match; | ||||
|         symbol               m_case; | ||||
|         symbol               m_underscore; | ||||
| 
 | ||||
|         typedef std::pair<symbol, expr*> named_expr; | ||||
|  | @ -382,7 +383,9 @@ namespace smt2 { | |||
|                 next(); | ||||
|                 return; | ||||
|             } | ||||
|             throw parser_exception(msg); | ||||
|             std::ostringstream str; | ||||
|             str << msg << " got " << curr_id(); | ||||
|             throw parser_exception(str.str()); | ||||
|         } | ||||
| 
 | ||||
|         symbol const & curr_id() const { return m_scanner.get_id(); } | ||||
|  | @ -406,6 +409,7 @@ namespace smt2 { | |||
|         bool curr_id_is_underscore() const { SASSERT(curr_is_identifier()); return curr_id() == m_underscore; } | ||||
|         bool curr_id_is_as() const { SASSERT(curr_is_identifier()); return curr_id() == m_as; } | ||||
|         bool curr_id_is_match() const { SASSERT(curr_is_identifier()); return curr_id() == m_match; } | ||||
|         bool curr_id_is_case() const { return curr_id() == m_case; } | ||||
|         bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; } | ||||
|         bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; } | ||||
|         bool curr_id_is_lambda() const { SASSERT(curr_is_identifier()); return curr_id() == m_lambda; } | ||||
|  | @ -625,8 +629,6 @@ namespace smt2 { | |||
|                 args.push_back(u); | ||||
|                 next(); | ||||
|             } | ||||
|             if (args.empty()) | ||||
|                 throw parser_exception("invalid indexed sort, index expected"); | ||||
|             sort * r = d->instantiate(pm(), args.size(), args.c_ptr()); | ||||
|             if (r == nullptr) | ||||
|                 throw parser_exception("invalid sort application"); | ||||
|  | @ -1321,7 +1323,13 @@ namespace smt2 { | |||
| 
 | ||||
|         /**
 | ||||
|          * SMT-LIB 2.6 pattern matches are of the form | ||||
|          * (match t ((p1 t1) ... (pm+1 tm+1)))          | ||||
|          * | ||||
|          *   (match t ((p1 t1) ... (pm+1 tm+1)))          | ||||
|          * | ||||
|          * precursor form is | ||||
|          * | ||||
|          *   (match t (case p1 t1) (case p2 t2) ... ) | ||||
|          * | ||||
|          */ | ||||
|         void push_match_frame() { | ||||
|             SASSERT(curr_is_identifier()); | ||||
|  | @ -1338,21 +1346,42 @@ namespace smt2 { | |||
|             sort* srt = m().get_sort(t); | ||||
| 
 | ||||
|             check_lparen_next("pattern bindings should be enclosed in a parenthesis"); | ||||
|             while (!curr_is_rparen()) { | ||||
|                 m_env.begin_scope(); | ||||
|                 unsigned num_bindings = m_num_bindings; | ||||
|                 check_lparen_next("invalid pattern binding, '(' expected"); | ||||
|                 parse_match_pattern(srt);   | ||||
|                 patterns.push_back(expr_stack().back()); | ||||
|                 expr_stack().pop_back(); | ||||
|                 parse_expr(); | ||||
|                 cases.push_back(expr_stack().back()); | ||||
|                 expr_stack().pop_back(); | ||||
|                 m_num_bindings = num_bindings; | ||||
|                 m_env.end_scope(); | ||||
|                 check_rparen_next("invalid pattern binding, ')' expected"); | ||||
|             if (curr_id_is_case()) { | ||||
|                 while (curr_id_is_case()) { | ||||
|                     next(); | ||||
|                     m_env.begin_scope(); | ||||
|                     unsigned num_bindings = m_num_bindings; | ||||
|                     parse_match_pattern(srt);   | ||||
|                     patterns.push_back(expr_stack().back()); | ||||
|                     expr_stack().pop_back(); | ||||
|                     parse_expr(); | ||||
|                     cases.push_back(expr_stack().back()); | ||||
|                     expr_stack().pop_back(); | ||||
|                     m_num_bindings = num_bindings; | ||||
|                     m_env.end_scope(); | ||||
|                     check_rparen_next("invalid pattern binding, ')' expected");                     | ||||
|                     if (curr_is_lparen()) { | ||||
|                         next(); | ||||
|                     } | ||||
|                 }                | ||||
|             } | ||||
|             else { | ||||
|                 while (!curr_is_rparen()) { | ||||
|                     m_env.begin_scope(); | ||||
|                     unsigned num_bindings = m_num_bindings; | ||||
|                     parse_match_pattern(srt);   | ||||
|                     patterns.push_back(expr_stack().back()); | ||||
|                     expr_stack().pop_back(); | ||||
|                     check_lparen_next("invalid pattern binding, '(' expected");                     | ||||
|                     parse_expr(); | ||||
|                     cases.push_back(expr_stack().back()); | ||||
|                     expr_stack().pop_back(); | ||||
|                     m_num_bindings = num_bindings; | ||||
|                     m_env.end_scope(); | ||||
|                     check_rparen_next("invalid pattern binding, ')' expected"); | ||||
|                 } | ||||
|                 next(); | ||||
|             } | ||||
|             next(); | ||||
|             m_num_expr_frames = num_frames + 1; | ||||
|             expr_stack().push_back(compile_patterns(t, patterns, cases)); | ||||
|         } | ||||
|  | @ -1410,6 +1439,12 @@ namespace smt2 { | |||
|         // compute match condition and substitution
 | ||||
|         // t is shifted by size of subst.
 | ||||
|         expr_ref bind_match(expr* t, expr* pattern, expr_ref_vector& subst) { | ||||
|             if (m().get_sort(t) != m().get_sort(pattern)) { | ||||
|                 std::ostringstream str; | ||||
|                 str << "sorts of pattern " << expr_ref(pattern, m()) << " and term "  | ||||
|                     << expr_ref(t, m()) << " are not aligned"; | ||||
|                 throw parser_exception(str.str()); | ||||
|             } | ||||
|             expr_ref tsh(m()); | ||||
|             if (is_var(pattern)) { | ||||
|                 shifter()(t, 1, tsh); | ||||
|  | @ -1520,11 +1555,22 @@ namespace smt2 { | |||
|             check_identifier("invalid indexed identifier, symbol expected"); | ||||
|             symbol r = curr_id(); | ||||
|             next(); | ||||
|             unsigned num_indices = 0; | ||||
|             while (!curr_is_rparen()) { | ||||
|                 if (curr_is_int()) { | ||||
|                     unsigned u = curr_unsigned(); | ||||
|                     m_param_stack.push_back(parameter(u)); | ||||
|                     if (!curr_numeral().is_unsigned()) { | ||||
|                         m_param_stack.push_back(parameter(curr_numeral()));                        | ||||
|                     } | ||||
|                     else { | ||||
|                         m_param_stack.push_back(parameter(curr_unsigned())); | ||||
|                     } | ||||
|                     next(); | ||||
|                 } | ||||
|                 else if (curr_is_float()) { | ||||
|                     m_param_stack.push_back(parameter(curr_numeral())); | ||||
|                     next(); | ||||
|                 } | ||||
|                 else if (curr_is_keyword()) { | ||||
|                     m_param_stack.push_back(parameter(curr_id())); | ||||
|                     next(); | ||||
|                 } | ||||
|                 else if (curr_is_identifier() || curr_is_lparen()) { | ||||
|  | @ -1533,10 +1579,7 @@ namespace smt2 { | |||
|                 else { | ||||
|                     throw parser_exception("invalid indexed identifier, integer, identifier or '(' expected"); | ||||
|                 } | ||||
|                 num_indices++; | ||||
|             } | ||||
|             if (num_indices == 0) | ||||
|                 throw parser_exception("invalid indexed identifier, index expected"); | ||||
|             next(); | ||||
|             return r; | ||||
|         } | ||||
|  | @ -1857,13 +1900,31 @@ namespace smt2 { | |||
|             unsigned num_args    = expr_stack().size() - fr->m_expr_spos; | ||||
|             unsigned num_indices = m_param_stack.size() - fr->m_param_spos; | ||||
|             expr_ref t_ref(m()); | ||||
|             m_ctx.mk_app(fr->m_f, | ||||
|                          num_args, | ||||
|                          expr_stack().c_ptr() + fr->m_expr_spos, | ||||
|                          num_indices, | ||||
|                          m_param_stack.c_ptr() + fr->m_param_spos, | ||||
|                          fr->m_as_sort ? sort_stack().back() : nullptr, | ||||
|                          t_ref); | ||||
|             local l; | ||||
|             if (m_env.find(fr->m_f, l)) { | ||||
|                 push_local(l); | ||||
|                 t_ref = expr_stack().back(); | ||||
|                 for (unsigned i = 0; i < num_args; ++i) { | ||||
|                     expr* arg = expr_stack().get(fr->m_expr_spos + i); | ||||
|                     expr* args[2] = { t_ref.get(), arg }; | ||||
|                     m_ctx.mk_app(symbol("select"),  | ||||
|                                  2,  | ||||
|                                  args, | ||||
|                                  0, | ||||
|                                  nullptr, | ||||
|                                  nullptr, | ||||
|                                  t_ref); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 m_ctx.mk_app(fr->m_f, | ||||
|                              num_args, | ||||
|                              expr_stack().c_ptr() + fr->m_expr_spos, | ||||
|                              num_indices, | ||||
|                              m_param_stack.c_ptr() + fr->m_param_spos, | ||||
|                              fr->m_as_sort ? sort_stack().back() : nullptr, | ||||
|                              t_ref); | ||||
|             } | ||||
|             expr_stack().shrink(fr->m_expr_spos); | ||||
|             m_param_stack.shrink(fr->m_param_spos); | ||||
|             if (fr->m_as_sort) | ||||
|  | @ -2216,10 +2277,14 @@ namespace smt2 { | |||
|             parse_expr(); | ||||
|             if (m().get_sort(expr_stack().back()) != sort_stack().back()) | ||||
|                 throw parser_exception("invalid function/constant definition, sort mismatch"); | ||||
|             if (is_fun)  | ||||
|                 m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); | ||||
|             else  | ||||
|                 m_ctx.model_add(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); | ||||
|             sort* const* sorts = sort_stack().c_ptr() + sort_spos; | ||||
|             expr* t = expr_stack().back(); | ||||
|             if (is_fun) { | ||||
|                 m_ctx.insert(id, num_vars, sorts, t); | ||||
|             } | ||||
|             else { | ||||
|                 m_ctx.model_add(id, num_vars, sorts, t); | ||||
|             } | ||||
|             check_rparen("invalid function/constant definition, ')' expected"); | ||||
|             // restore stacks & env
 | ||||
|             symbol_stack().shrink(sym_spos); | ||||
|  | @ -2311,7 +2376,7 @@ namespace smt2 { | |||
|             next(); | ||||
|         } | ||||
| 
 | ||||
|         void parse_rec_fun_decl(func_decl_ref& f, expr_ref_vector& bindings, svector<symbol>& ids) { | ||||
|         recfun::promise_def parse_rec_fun_decl(func_decl_ref& f, expr_ref_vector& bindings, svector<symbol>& ids) { | ||||
|             SASSERT(m_num_bindings == 0); | ||||
|             check_identifier("invalid function/constant definition, symbol expected"); | ||||
|             symbol id = curr_id(); | ||||
|  | @ -2322,7 +2387,8 @@ namespace smt2 { | |||
|             unsigned num_vars  = parse_sorted_vars(); | ||||
|             SASSERT(num_vars == m_num_bindings); | ||||
|             parse_sort("Invalid recursive function definition"); | ||||
|             f = m().mk_func_decl(id, num_vars, sort_stack().c_ptr() + sort_spos, sort_stack().back()); | ||||
|             recfun::promise_def pdef = m_ctx.decl_rec_fun(id, num_vars, sort_stack().c_ptr() + sort_spos, sort_stack().back()); | ||||
|             f = pdef.get_def()->get_decl(); | ||||
|             bindings.append(num_vars, expr_stack().c_ptr() + expr_spos); | ||||
|             ids.append(num_vars, symbol_stack().c_ptr() + sym_spos); | ||||
|             symbol_stack().shrink(sym_spos); | ||||
|  | @ -2330,6 +2396,7 @@ namespace smt2 { | |||
|             expr_stack().shrink(expr_spos); | ||||
|             m_env.end_scope(); | ||||
|             m_num_bindings = 0; | ||||
|             return pdef; | ||||
|         } | ||||
| 
 | ||||
|         void parse_rec_fun_bodies(func_decl_ref_vector const& decls, vector<expr_ref_vector> const& bindings, vector<svector<symbol> >const & ids) { | ||||
|  | @ -3005,6 +3072,7 @@ namespace smt2 { | |||
|             m_define_fun_rec("define-fun-rec"), | ||||
|             m_define_funs_rec("define-funs-rec"), | ||||
|             m_match("match"), | ||||
|             m_case("case"), | ||||
|             m_underscore("_"), | ||||
|             m_num_open_paren(0), | ||||
|             m_current_file(filename) { | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -25,6 +25,7 @@ Revision History: | |||
| #include "sat/sat_solver.h" | ||||
| #include "sat/sat_lookahead.h" | ||||
| #include "sat/sat_unit_walk.h" | ||||
| #include "sat/sat_big.h" | ||||
| #include "util/scoped_ptr_vector.h" | ||||
| #include "util/sorting_network.h" | ||||
| 
 | ||||
|  | @ -41,8 +42,11 @@ namespace sat { | |||
|             unsigned m_num_bin_subsumes; | ||||
|             unsigned m_num_clause_subsumes; | ||||
|             unsigned m_num_pb_subsumes; | ||||
|             unsigned m_num_big_strengthenings; | ||||
|             unsigned m_num_cut; | ||||
|             unsigned m_num_gc; | ||||
|             unsigned m_num_overflow; | ||||
|             unsigned m_num_lemmas; | ||||
|             stats() { reset(); } | ||||
|             void reset() { memset(this, 0, sizeof(*this)); } | ||||
|         }; | ||||
|  | @ -57,6 +61,7 @@ namespace sat { | |||
|         class card; | ||||
|         class pb; | ||||
|         class xr; | ||||
|         class pb_base; | ||||
| 
 | ||||
|         class constraint { | ||||
|         protected: | ||||
|  | @ -104,6 +109,7 @@ namespace sat { | |||
|             card const& to_card() const; | ||||
|             pb const&  to_pb() const; | ||||
|             xr const& to_xr() const; | ||||
|             pb_base const& to_pb_base() const;  | ||||
|             bool is_card() const { return m_tag == card_t; } | ||||
|             bool is_pb() const { return m_tag == pb_t; } | ||||
|             bool is_xr() const { return m_tag == xr_t; } | ||||
|  | @ -118,7 +124,7 @@ namespace sat { | |||
|         }; | ||||
| 
 | ||||
|         friend std::ostream& operator<<(std::ostream& out, constraint const& c); | ||||
| 
 | ||||
|          | ||||
|         // base class for pb and cardinality constraints
 | ||||
|         class pb_base : public constraint { | ||||
|         protected: | ||||
|  | @ -204,12 +210,18 @@ namespace sat { | |||
|     protected: | ||||
| 
 | ||||
|         struct ineq { | ||||
|             literal_vector    m_lits; | ||||
|             svector<uint64_t> m_coeffs; | ||||
|             svector<wliteral> m_wlits; | ||||
|             uint64_t          m_k; | ||||
|             ineq(): m_k(0) {} | ||||
|             void reset(uint64_t k) { m_lits.reset(); m_coeffs.reset(); m_k = k; } | ||||
|             void push(literal l, uint64_t c) { m_lits.push_back(l); m_coeffs.push_back(c); } | ||||
|             unsigned size() const { return m_wlits.size(); } | ||||
|             literal lit(unsigned i) const { return m_wlits[i].second; } | ||||
|             unsigned coeff(unsigned i) const { return m_wlits[i].first; } | ||||
|             void reset(uint64_t k) { m_wlits.reset(); m_k = k; } | ||||
|             void push(literal l, unsigned c) { m_wlits.push_back(wliteral(c,l)); } | ||||
|             unsigned bv_coeff(bool_var v) const; | ||||
|             void divide(unsigned c); | ||||
|             void weaken(unsigned i); | ||||
|             bool contains(literal l) const { for (auto wl : m_wlits) if (wl.second == l) return true; return false; } | ||||
|         }; | ||||
| 
 | ||||
|         solver*                m_solver; | ||||
|  | @ -273,7 +285,8 @@ namespace sat { | |||
| 
 | ||||
|         // simplification routines
 | ||||
| 
 | ||||
|         svector<bool>             m_visited; | ||||
|         svector<unsigned>             m_visited; | ||||
|         unsigned                      m_visited_ts; | ||||
|         vector<svector<constraint*>>    m_cnstr_use_list; | ||||
|         use_list                  m_clause_use_list; | ||||
|         bool                      m_simplify_change; | ||||
|  | @ -292,9 +305,11 @@ namespace sat { | |||
|         void binary_subsumption(card& c1, literal lit); | ||||
|         void clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses); | ||||
|         void card_subsumption(card& c1, literal lit); | ||||
|         void mark_visited(literal l) { m_visited[l.index()] = true; } | ||||
|         void unmark_visited(literal l) { m_visited[l.index()] = false; } | ||||
|         bool is_marked(literal l) const { return m_visited[l.index()] != 0; } | ||||
|         void init_visited(); | ||||
|         void mark_visited(literal l) { m_visited[l.index()] = m_visited_ts; } | ||||
|         void mark_visited(bool_var v) { mark_visited(literal(v, false)); } | ||||
|         bool is_visited(bool_var v) const { return is_visited(literal(v, false)); } | ||||
|         bool is_visited(literal l) const { return m_visited[l.index()] == m_visited_ts; } | ||||
|         unsigned get_num_unblocked_bin(literal l); | ||||
|         literal get_min_occurrence_literal(card const& c); | ||||
|         void init_use_lists(); | ||||
|  | @ -302,6 +317,9 @@ namespace sat { | |||
|         unsigned set_non_external(); | ||||
|         unsigned elim_pure(); | ||||
|         bool elim_pure(literal lit); | ||||
|         void unit_strengthen(); | ||||
|         void unit_strengthen(big& big, constraint& cs); | ||||
|         void unit_strengthen(big& big, pb_base& p); | ||||
|         void subsumption(constraint& c1); | ||||
|         void subsumption(card& c1); | ||||
|         void gc_half(char const* _method); | ||||
|  | @ -396,10 +414,23 @@ namespace sat { | |||
|         lbool eval(model const& m, pb const& p) const; | ||||
|         double get_reward(pb const& p, literal_occs_fun& occs) const; | ||||
| 
 | ||||
|         // RoundingPb conflict resolution
 | ||||
|         lbool resolve_conflict_rs(); | ||||
|         void round_to_one(ineq& ineq, bool_var v); | ||||
|         void round_to_one(bool_var v); | ||||
|         void divide(unsigned c); | ||||
|         void resolve_on(literal lit); | ||||
|         void resolve_with(ineq const& ineq); | ||||
|         void reset_marks(unsigned idx); | ||||
|         void mark_variables(ineq const& ineq); | ||||
| 
 | ||||
|         void bail_resolve_conflict(unsigned idx);         | ||||
| 
 | ||||
|         // access solver
 | ||||
|         inline lbool value(bool_var v) const { return value(literal(v, false)); } | ||||
|         inline lbool value(literal lit) const { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); } | ||||
|         inline lbool value(model const& m, literal l) const { return l.sign() ? ~m[l.var()] : m[l.var()]; } | ||||
|         inline bool is_false(literal lit) const { return l_false == value(lit); } | ||||
| 
 | ||||
|         inline unsigned lvl(literal lit) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(lit); } | ||||
|         inline unsigned lvl(bool_var v) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(v); } | ||||
|  | @ -426,9 +457,11 @@ namespace sat { | |||
| 
 | ||||
|         mutable bool m_overflow; | ||||
|         void reset_active_var_set(); | ||||
|         void normalize_active_coeffs(); | ||||
|         bool test_and_set_active(bool_var v); | ||||
|         void inc_coeff(literal l, unsigned offset); | ||||
|         int64_t get_coeff(bool_var v) const; | ||||
|         uint64_t get_coeff(literal lit) const; | ||||
|         wliteral get_wliteral(bool_var v); | ||||
|         unsigned get_abs_coeff(bool_var v) const;        | ||||
|         int   get_int_coeff(bool_var v) const; | ||||
|         unsigned get_bound() const; | ||||
|  | @ -436,6 +469,7 @@ namespace sat { | |||
| 
 | ||||
|         literal get_asserting_literal(literal conseq); | ||||
|         void process_antecedent(literal l, unsigned offset); | ||||
|         void process_antecedent(literal l) { process_antecedent(l, 1); } | ||||
|         void process_card(card& c, unsigned offset); | ||||
|         void cut(); | ||||
|         bool create_asserting_lemma(); | ||||
|  | @ -446,6 +480,7 @@ namespace sat { | |||
|         bool validate_conflict(pb const& p) const; | ||||
|         bool validate_assign(literal_vector const& lits, literal lit); | ||||
|         bool validate_lemma(); | ||||
|         bool validate_ineq(ineq const& ineq) const; | ||||
|         bool validate_unit_propagation(card const& c, literal alit) const; | ||||
|         bool validate_unit_propagation(pb const& p, literal alit) const; | ||||
|         bool validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const; | ||||
|  | @ -464,12 +499,17 @@ namespace sat { | |||
| 
 | ||||
|         ineq m_A, m_B, m_C; | ||||
|         void active2pb(ineq& p); | ||||
|         constraint* active2lemma(); | ||||
|         constraint* active2constraint(); | ||||
|         constraint* active2card(); | ||||
|         void active2wlits(); | ||||
|         void active2wlits(svector<wliteral>& wlits); | ||||
|         void justification2pb(justification const& j, literal lit, unsigned offset, ineq& p); | ||||
|         void constraint2pb(constraint& cnstr, literal lit, unsigned offset, ineq& p); | ||||
|         bool validate_resolvent(); | ||||
|         unsigned get_coeff(ineq const& pb, literal lit); | ||||
| 
 | ||||
|         void display(std::ostream& out, ineq& p, bool values = false) const; | ||||
|         void display(std::ostream& out, ineq const& p, bool values = false) const; | ||||
|         void display(std::ostream& out, card const& c, bool values) const; | ||||
|         void display(std::ostream& out, pb const& p, bool values) const; | ||||
|         void display(std::ostream& out, xr const& c, bool values) const; | ||||
|  |  | |||
|  | @ -22,7 +22,8 @@ Revision History: | |||
| namespace sat { | ||||
| 
 | ||||
|     big::big(random_gen& rand):  | ||||
|         m_rand(rand) { | ||||
|         m_rand(rand), | ||||
|         m_include_cardinality(false) { | ||||
|     } | ||||
| 
 | ||||
|     void big::init(solver& s, bool learned) { | ||||
|  | @ -42,22 +43,22 @@ namespace sat { | |||
|                     m_roots[v.index()] = false; | ||||
|                     edges.push_back(v); | ||||
|                 } | ||||
| #if 0 | ||||
|                 if (w.is_ext_constraint() &&  | ||||
|                 if (m_include_cardinality &&  | ||||
|                     w.is_ext_constraint() &&  | ||||
|                     s.m_ext &&  | ||||
|                     learned &&  | ||||
|                     learned && // cannot (yet) observe if ext constraints are learned
 | ||||
|                     !seen_idx.contains(w.get_ext_constraint_idx()) && | ||||
|                     s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), r)) { | ||||
|                     seen_idx.insert(w.get_ext_constraint_idx(), true); | ||||
|                     for (unsigned i = 0; i < r.size(); ++i) { | ||||
|                         literal u = r[i];  | ||||
|                         for (unsigned j = i + 1; j < r.size(); ++j) { | ||||
|                             // add ~r[i] -> r[j]
 | ||||
|                             literal v = r[j]; | ||||
|                             literal u = ~r[j]; | ||||
|                     for (unsigned i = 0; i < std::min(4u, r.size()); ++i) { | ||||
|                         shuffle<literal>(r.size(), r.c_ptr(), m_rand); | ||||
|                         literal u = r[0]; | ||||
|                         for (unsigned j = 1; j < r.size(); ++j) { | ||||
|                             literal v = ~r[j]; | ||||
|                             // add u -> v
 | ||||
|                             m_roots[v.index()] = false; | ||||
|                             m_dag[u.index()].push_back(v); | ||||
|                             // add ~r[j] -> r[i]
 | ||||
|                             // add ~v -> ~u
 | ||||
|                             v.neg(); | ||||
|                             u.neg(); | ||||
|                             m_roots[u.index()] = false; | ||||
|  | @ -65,7 +66,6 @@ namespace sat { | |||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| #endif | ||||
|             } | ||||
|         } | ||||
|         done_adding_edges(); | ||||
|  | @ -268,6 +268,16 @@ namespace sat { | |||
|         return out << v; | ||||
|     } | ||||
| 
 | ||||
|     literal big::get_root(literal l) { | ||||
|         literal r = l; | ||||
|         do { | ||||
|             l = r; | ||||
|             r = m_root[l.index()]; | ||||
|         } | ||||
|         while (r != l); | ||||
|         return r; | ||||
|     } | ||||
| 
 | ||||
|     void big::display(std::ostream& out) const { | ||||
|         unsigned idx = 0; | ||||
|         for (auto& next : m_dag) { | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ namespace sat { | |||
|         svector<int>           m_left, m_right; | ||||
|         literal_vector         m_root, m_parent; | ||||
|         bool                   m_learned; | ||||
|         bool                   m_include_cardinality; | ||||
| 
 | ||||
|         svector<std::pair<literal, literal>> m_del_bin; | ||||
|          | ||||
|  | @ -54,6 +55,9 @@ namespace sat { | |||
|         // static svector<std::pair<literal, literal>> s_del_bin;
 | ||||
| 
 | ||||
|         big(random_gen& rand); | ||||
| 
 | ||||
|         void set_include_cardinality(bool f) { m_include_cardinality = f; } | ||||
|          | ||||
|         /**
 | ||||
|            \brief initialize a BIG from a solver. | ||||
|          */ | ||||
|  | @ -77,7 +81,7 @@ namespace sat { | |||
|         int get_left(literal l) const { return m_left[l.index()]; } | ||||
|         int get_right(literal l) const { return m_right[l.index()]; } | ||||
|         literal get_parent(literal l) const { return m_parent[l.index()]; } | ||||
|         literal get_root(literal l) const { return m_root[l.index()]; } | ||||
|         literal get_root(literal l); | ||||
|         bool reaches(literal u, literal v) const { return m_left[u.index()] < m_left[v.index()] && m_right[v.index()] < m_right[u.index()]; }         | ||||
|         bool connected(literal u, literal v) const { return reaches(u, v) || reaches(~v, ~u); } | ||||
|         void display(std::ostream& out) const; | ||||
|  |  | |||
|  | @ -199,6 +199,22 @@ namespace sat { | |||
|         else  | ||||
|             throw sat_param_exception("invalid PB solver: solver, totalizer, circuit, sorting, segmented"); | ||||
| 
 | ||||
|         s = p.pb_resolve(); | ||||
|         if (s == "cardinality")  | ||||
|             m_pb_resolve = PB_CARDINALITY; | ||||
|         else if (s == "rounding")  | ||||
|             m_pb_resolve = PB_ROUNDING; | ||||
|         else  | ||||
|             throw sat_param_exception("invalid PB resolve: 'cardinality' or 'rounding' expected"); | ||||
| 
 | ||||
|         s = p.pb_lemma_format(); | ||||
|         if (s == "cardinality")  | ||||
|             m_pb_lemma_format = PB_LEMMA_CARDINALITY; | ||||
|         else if (s == "pb") | ||||
|             m_pb_lemma_format = PB_LEMMA_PB; | ||||
|         else | ||||
|             throw sat_param_exception("invalid PB lemma format: 'cardinality' or 'pb' expected"); | ||||
|          | ||||
|         m_card_solver = p.cardinality_solver(); | ||||
| 
 | ||||
|         sat_simplifier_params sp(_p); | ||||
|  |  | |||
|  | @ -60,6 +60,16 @@ namespace sat { | |||
|         PB_SEGMENTED | ||||
|     }; | ||||
| 
 | ||||
|     enum pb_resolve { | ||||
|         PB_CARDINALITY, | ||||
|         PB_ROUNDING | ||||
|     }; | ||||
| 
 | ||||
|     enum pb_lemma_format { | ||||
|         PB_LEMMA_CARDINALITY, | ||||
|         PB_LEMMA_PB | ||||
|     }; | ||||
| 
 | ||||
|     enum reward_t { | ||||
|         ternary_reward, | ||||
|         unit_literal_reward, | ||||
|  | @ -151,6 +161,8 @@ namespace sat { | |||
|          | ||||
|         pb_solver          m_pb_solver; | ||||
|         bool               m_card_solver; | ||||
|         pb_resolve         m_pb_resolve; | ||||
|         pb_lemma_format    m_pb_lemma_format; | ||||
|          | ||||
|         // branching heuristic settings.
 | ||||
|         branching_heuristic m_branching_heuristic; | ||||
|  |  | |||
|  | @ -43,8 +43,10 @@ def_module_params('sat', | |||
|                           ('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'), | ||||
|                           ('cardinality.solver', BOOL, True, 'use cardinality solver'), | ||||
|                           ('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), solver (use native solver)'), | ||||
|                           ('xor.solver', BOOL, False, 'use xor solver'), | ||||
|                           ('xor.solver', BOOL, False, 'use xor solver'),                         | ||||
|                           ('cardinality.encoding', SYMBOL, 'grouped', 'encoding used for at-most-k constraints: grouped, bimander, ordered, unate, circuit'), | ||||
|                           ('pb.resolve', SYMBOL, 'cardinality', 'resolution strategy for boolean algebra solver: cardinality, rounding'), | ||||
|                           ('pb.lemma_format', SYMBOL, 'cardinality', 'generate either cardinality or pb lemmas'), | ||||
|                           ('local_search', BOOL, False, 'use local search instead of CDCL'), | ||||
|                           ('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'), | ||||
|                           ('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'), | ||||
|  |  | |||
|  | @ -60,7 +60,6 @@ namespace sat { | |||
|         void ensure_big(bool learned) { m_big.ensure_big(m_solver, learned); } | ||||
|         int get_left(literal l) const { return m_big.get_left(l); } | ||||
|         int get_right(literal l) const { return m_big.get_right(l); } | ||||
|         literal get_root(literal l) const { return m_big.get_root(l); } | ||||
|         bool connected(literal u, literal v) const { return m_big.connected(u, v); } | ||||
|     }; | ||||
| }; | ||||
|  |  | |||
|  | @ -343,12 +343,6 @@ namespace sat { | |||
|     } | ||||
| 
 | ||||
|     void solver::mk_bin_clause(literal l1, literal l2, bool learned) { | ||||
| #if 0 | ||||
|         if ((l1.var() == 2039 || l2.var() == 2039) && | ||||
|             (l1.var() == 27042 || l2.var() == 27042)) {  | ||||
|             IF_VERBOSE(1, verbose_stream() << "mk_bin: " << l1 << " " << l2 << " " << learned << "\n"); | ||||
|         } | ||||
| #endif | ||||
|         if (find_binary_watch(get_wlist(~l1), ~l2)) { | ||||
|             assign(l1, justification()); | ||||
|             return; | ||||
|  |  | |||
|  | @ -82,6 +82,7 @@ struct goal2sat::imp { | |||
|         m_is_lemma(false) { | ||||
|         updt_params(p); | ||||
|         m_true = sat::null_bool_var; | ||||
|         mk_true(); | ||||
|     } | ||||
|          | ||||
|     void updt_params(params_ref const & p) { | ||||
|  |  | |||
|  | @ -56,9 +56,11 @@ z3_add_component(smt | |||
|     theory_dl.cpp | ||||
|     theory_dummy.cpp | ||||
|     theory_fpa.cpp | ||||
|     theory_jobscheduler.cpp | ||||
|     theory_lra.cpp | ||||
|     theory_opt.cpp | ||||
|     theory_pb.cpp | ||||
|     theory_recfun.cpp | ||||
|     theory_seq.cpp | ||||
|     theory_str.cpp | ||||
|     theory_utvpi.cpp | ||||
|  |  | |||
|  | @ -95,5 +95,7 @@ def_module_params(module_name='smt', | |||
|                           ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core'), | ||||
|                           ('core.extend_nonlocal_patterns', BOOL, False, 'extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier\'s body'), | ||||
|                           ('lemma_gc_strategy', UINT, 0, 'lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none'), | ||||
|                           ('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy') | ||||
|                           ('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy'), | ||||
|                           ('recfun.native', BOOL, True, 'use native rec-fun solver'), | ||||
|                           ('recfun.depth', UINT, 2, 'initial depth for maxrec expansion') | ||||
|                           )) | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ Revision History: | |||
| #include "model/model_pp.h" | ||||
| #include "ast/ast_smt2_pp.h" | ||||
| #include "ast/ast_translation.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| 
 | ||||
| namespace smt { | ||||
| 
 | ||||
|  | @ -1384,7 +1385,10 @@ namespace smt { | |||
|                 SASSERT(m_manager.is_eq(n)); | ||||
|                 expr * lhs = n->get_arg(0); | ||||
|                 expr * rhs = n->get_arg(1); | ||||
|                 if (val == l_true) { | ||||
|                 if (m_manager.is_bool(lhs)) { | ||||
|                     // no-op
 | ||||
|                 } | ||||
|                 else if (val == l_true) { | ||||
|                     add_eq(get_enode(lhs), get_enode(rhs), eq_justification(l)); | ||||
|                 } | ||||
|                 else { | ||||
|  | @ -3207,7 +3211,7 @@ namespace smt { | |||
|             } | ||||
|             else { | ||||
|                 set_conflict(b_justification(tmp_clause.first), null_literal); | ||||
|             }		 | ||||
|             } | ||||
|             VERIFY(!resolve_conflict()); | ||||
|             return l_false; | ||||
|         next_clause: | ||||
|  | @ -3266,6 +3270,18 @@ namespace smt { | |||
|         m_assumptions.reset(); | ||||
|     } | ||||
| 
 | ||||
|     bool context::should_research(lbool r) { | ||||
|         if (r != l_false || m_unsat_core.empty()) {  | ||||
|             return false; | ||||
|         } | ||||
|         for (theory* th : m_theory_set) { | ||||
|             if (th->should_research(m_unsat_core)) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     lbool context::mk_unsat_core(lbool r) {         | ||||
|         if (r != l_false) return r; | ||||
|         SASSERT(inconsistent()); | ||||
|  | @ -3353,7 +3369,7 @@ namespace smt { | |||
|         add_theory_assumptions(theory_assumptions); | ||||
|         if (!theory_assumptions.empty()) { | ||||
|             TRACE("search", tout << "Adding theory assumptions to context" << std::endl;); | ||||
|             return check(theory_assumptions.size(), theory_assumptions.c_ptr(), reset_cancel, true); | ||||
|             return check(0, nullptr, reset_cancel); | ||||
|         } | ||||
| 
 | ||||
|         internalize_assertions(); | ||||
|  | @ -3407,19 +3423,24 @@ namespace smt { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel, bool already_did_theory_assumptions) { | ||||
|     lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel) { | ||||
|         if (!check_preamble(reset_cancel)) return l_undef; | ||||
|         SASSERT(at_base_level()); | ||||
|         setup_context(false); | ||||
|         expr_ref_vector asms(m_manager, num_assumptions, assumptions); | ||||
|         if (!already_did_theory_assumptions) add_theory_assumptions(asms);                 | ||||
|         // introducing proxies: if (!validate_assumptions(asms)) return l_undef;
 | ||||
|         TRACE("unsat_core_bug", tout << asms << "\n";);         | ||||
|         internalize_assertions(); | ||||
|         init_assumptions(asms); | ||||
|         TRACE("before_search", display(tout);); | ||||
|         lbool r = search(); | ||||
|         r = mk_unsat_core(r);         | ||||
|         lbool r; | ||||
|         do { | ||||
|             pop_to_base_lvl(); | ||||
|             expr_ref_vector asms(m_manager, num_assumptions, assumptions); | ||||
|             add_theory_assumptions(asms);                 | ||||
|             // introducing proxies: if (!validate_assumptions(asms)) return l_undef;
 | ||||
|             TRACE("unsat_core_bug", tout << asms << "\n";);         | ||||
|             internalize_assertions(); | ||||
|             init_assumptions(asms); | ||||
|             TRACE("before_search", display(tout);); | ||||
|             r = search(); | ||||
|             r = mk_unsat_core(r);         | ||||
|         } | ||||
|         while (should_research(r)); | ||||
|         r = check_finalize(r); | ||||
|         return r; | ||||
|     } | ||||
|  | @ -3428,15 +3449,20 @@ namespace smt { | |||
|         if (!check_preamble(true)) return l_undef; | ||||
|         TRACE("before_search", display(tout);); | ||||
|         setup_context(false); | ||||
|         expr_ref_vector asms(cube); | ||||
|         add_theory_assumptions(asms); | ||||
|         // introducing proxies: if (!validate_assumptions(asms)) return l_undef;
 | ||||
|         for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; | ||||
|         internalize_assertions(); | ||||
|         init_assumptions(asms); | ||||
|         for (auto const& clause : clauses) init_clause(clause); | ||||
|         lbool r = search();    | ||||
|         r = mk_unsat_core(r);              | ||||
|         lbool r; | ||||
|         do { | ||||
|             pop_to_base_lvl(); | ||||
|             expr_ref_vector asms(cube); | ||||
|             add_theory_assumptions(asms); | ||||
|             // introducing proxies: if (!validate_assumptions(asms)) return l_undef;
 | ||||
|             for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; | ||||
|             internalize_assertions(); | ||||
|             init_assumptions(asms); | ||||
|             for (auto const& clause : clauses) init_clause(clause); | ||||
|             r = search();    | ||||
|             r = mk_unsat_core(r);              | ||||
|         } | ||||
|         while (should_research(r)); | ||||
|         r = check_finalize(r); | ||||
|         return r;            | ||||
|     } | ||||
|  | @ -3770,7 +3796,7 @@ namespace smt { | |||
|         } | ||||
| 
 | ||||
|         m_stats.m_num_final_checks++; | ||||
| 		TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";); | ||||
|         TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";); | ||||
| 
 | ||||
|         final_check_status ok = m_qmanager->final_check_eh(false); | ||||
|         if (ok != FC_DONE) | ||||
|  | @ -4428,6 +4454,26 @@ namespace smt { | |||
|                 m_model->register_decl(f, fi); | ||||
|             } | ||||
|         } | ||||
|         recfun::util u(m); | ||||
|         recfun::decl::plugin& p = u.get_plugin(); | ||||
|         func_decl_ref_vector recfuns = u.get_rec_funs(); | ||||
|         for (func_decl* f : recfuns) { | ||||
|             auto& def = u.get_def(f); | ||||
|             expr* rhs = def.get_rhs(); | ||||
|             if (!rhs) continue; | ||||
|             func_interp* fi = alloc(func_interp, m, f->get_arity()); | ||||
|             // reverse argument order so that variable 0 starts at the beginning.
 | ||||
|             expr_ref_vector subst(m); | ||||
|             unsigned idx = 0; | ||||
|             for (unsigned i = 0; i < f->get_arity(); ++i) { | ||||
|                 subst.push_back(m.mk_var(i, f->get_domain(i))); | ||||
|             } | ||||
|             var_subst sub(m, true); | ||||
|             expr_ref bodyr = sub(rhs, subst.size(), subst.c_ptr()); | ||||
| 
 | ||||
|             fi->set_else(bodyr); | ||||
|             m_model->register_decl(f, fi); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -859,6 +859,10 @@ namespace smt { | |||
| 
 | ||||
|         void mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params = 0, parameter * params = nullptr); | ||||
| 
 | ||||
|         void mk_th_axiom(theory_id tid, literal_vector const& ls, unsigned num_params = 0, parameter * params = nullptr) { | ||||
|             mk_th_axiom(tid, ls.size(), ls.c_ptr(), num_params, params); | ||||
|         } | ||||
| 
 | ||||
|         /*
 | ||||
|          * Provide a hint to the core solver that the specified literals form a "theory case split". | ||||
|          * The core solver will enforce the condition that exactly one of these literals can be | ||||
|  | @ -1010,9 +1014,9 @@ namespace smt { | |||
|         void restore_theory_vars(enode * r2, enode * r1); | ||||
| 
 | ||||
|         void push_eq(enode * lhs, enode * rhs, eq_justification const & js) { | ||||
|             SASSERT(lhs != rhs); | ||||
|             SASSERT(lhs->get_root() != rhs->get_root()); | ||||
|             m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js)); | ||||
|             if (lhs->get_root() != rhs->get_root()) { | ||||
|                 m_eq_propagation_queue.push_back(new_eq(lhs, rhs, js)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         void push_new_congruence(enode * n1, enode * n2, bool used_commutativity) { | ||||
|  | @ -1133,6 +1137,8 @@ namespace smt { | |||
|         void add_theory_assumptions(expr_ref_vector & theory_assumptions); | ||||
| 
 | ||||
|         lbool mk_unsat_core(lbool result); | ||||
|          | ||||
|         bool should_research(lbool result); | ||||
| 
 | ||||
|         void validate_unsat_core(); | ||||
| 
 | ||||
|  | @ -1243,6 +1249,11 @@ namespace smt { | |||
|     public: | ||||
|         bool can_propagate() const; | ||||
| 
 | ||||
|         // Retrieve arithmetic values. 
 | ||||
|         bool get_arith_lo(expr* e, rational& lo, bool& strict); | ||||
|         bool get_arith_up(expr* e, rational& up, bool& strict); | ||||
|         bool get_arith_value(expr* e, rational& value); | ||||
| 
 | ||||
|         // -----------------------------------
 | ||||
|         //
 | ||||
|         // Model checking... (must be improved)
 | ||||
|  | @ -1515,7 +1526,7 @@ namespace smt { | |||
| 
 | ||||
|         void pop(unsigned num_scopes); | ||||
| 
 | ||||
|         lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true, bool already_did_theory_assumptions = false); | ||||
|         lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true); | ||||
| 
 | ||||
|         lbool check(expr_ref_vector const& cube, vector<expr_ref_vector> const& clauses); | ||||
| 
 | ||||
|  | @ -1609,6 +1620,34 @@ namespace smt { | |||
|         void insert_macro(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep) { m_asserted_formulas.insert_macro(f, m, pr, dep); } | ||||
|     }; | ||||
| 
 | ||||
|     struct pp_lit { | ||||
|         context & ctx; | ||||
|         literal lit; | ||||
|         pp_lit(context & ctx, literal lit) : ctx(ctx), lit(lit) {} | ||||
|     }; | ||||
| 
 | ||||
|     inline std::ostream & operator<<(std::ostream & out, pp_lit const & pp) { | ||||
|         return pp.ctx.display_detailed_literal(out, pp.lit); | ||||
|     } | ||||
| 
 | ||||
|     struct pp_lits { | ||||
|         context & ctx; | ||||
|         literal const *lits; | ||||
|         unsigned len; | ||||
|         pp_lits(context & ctx, unsigned len, literal const *lits) : ctx(ctx), lits(lits), len(len) {} | ||||
|         pp_lits(context & ctx, literal_vector const& ls) : ctx(ctx), lits(ls.c_ptr()), len(ls.size()) {} | ||||
|     }; | ||||
| 
 | ||||
|     inline std::ostream & operator<<(std::ostream & out, pp_lits const & pp) { | ||||
|         out << "{"; | ||||
|         bool first = true; | ||||
|         for (unsigned i = 0; i < pp.len; ++i) { | ||||
|             if (first) { first = false; } else { out << " or\n"; } | ||||
|             pp.ctx.display_detailed_literal(out, pp.lits[i]); | ||||
|         } | ||||
|         return out << "}"; | ||||
| 
 | ||||
|     } | ||||
|     struct enode_eq_pp { | ||||
|         context const&          ctx; | ||||
|         enode_pair const& p; | ||||
|  |  | |||
|  | @ -303,7 +303,7 @@ namespace smt { | |||
|         for (bool_var v = 0; v < num; v++) { | ||||
|             if (has_enode(v)) { | ||||
|                 enode * n = bool_var2enode(v); | ||||
|                 if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false) { | ||||
|                 if (n->is_eq() && is_relevant(n) && get_assignment(v) == l_false && !m_manager.is_iff(n->get_owner())) { | ||||
|                     TRACE("check_th_diseq_propagation", tout << "checking: #" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m_manager) << "\n";); | ||||
|                     enode * lhs = n->get_arg(0)->get_root(); | ||||
|                     enode * rhs = n->get_arg(1)->get_root(); | ||||
|  |  | |||
|  | @ -402,7 +402,10 @@ namespace smt { | |||
|             enode * n         = m_context->get_enode(t); | ||||
|             unsigned num_args = n->get_num_args(); | ||||
|             func_decl * f     = n->get_decl(); | ||||
|             if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) { | ||||
|             if (num_args == 0 && include_func_interp(f)) { | ||||
|                 m_model->register_decl(f, get_value(n)); | ||||
|             } | ||||
|             else if (num_args > 0 && n->get_cg() == n && include_func_interp(f)) { | ||||
|                 ptr_buffer<expr> args; | ||||
|                 expr * result = get_value(n); | ||||
|                 SASSERT(result); | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ Revision History: | |||
| #include "smt/theory_array_full.h" | ||||
| #include "smt/theory_bv.h" | ||||
| #include "smt/theory_datatype.h" | ||||
| #include "smt/theory_recfun.h" | ||||
| #include "smt/theory_dummy.h" | ||||
| #include "smt/theory_dl.h" | ||||
| #include "smt/theory_seq_empty.h" | ||||
|  | @ -35,6 +36,7 @@ Revision History: | |||
| #include "smt/theory_pb.h" | ||||
| #include "smt/theory_fpa.h" | ||||
| #include "smt/theory_str.h" | ||||
| #include "smt/theory_jobscheduler.h" | ||||
| 
 | ||||
| namespace smt { | ||||
| 
 | ||||
|  | @ -119,6 +121,8 @@ namespace smt { | |||
|             setup_UFLRA(); | ||||
|         else if (m_logic == "LRA") | ||||
|             setup_LRA(); | ||||
|         else if (m_logic == "CSP") | ||||
|             setup_CSP(); | ||||
|         else if (m_logic == "QF_FP") | ||||
|             setup_QF_FP(); | ||||
|         else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") | ||||
|  | @ -196,6 +200,8 @@ namespace smt { | |||
|                 setup_QF_DT(); | ||||
|             else if (m_logic == "LRA") | ||||
|                 setup_LRA(); | ||||
|             else if (m_logic == "CSP") | ||||
|                 setup_CSP(); | ||||
|             else  | ||||
|                 setup_unknown(st); | ||||
|         } | ||||
|  | @ -217,6 +223,7 @@ namespace smt { | |||
|     void setup::setup_QF_DT() { | ||||
|         setup_QF_UF(); | ||||
|         setup_datatypes(); | ||||
|         setup_recfuns(); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_QF_BVRE() { | ||||
|  | @ -873,6 +880,12 @@ namespace smt { | |||
|         m_context.register_plugin(alloc(theory_datatype, m_manager, m_params)); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_recfuns() { | ||||
|         TRACE("recfun", tout << "registering theory recfun...\n";); | ||||
|         theory_recfun * th = alloc(theory_recfun, m_manager); | ||||
|         m_context.register_plugin(th); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_dl() { | ||||
|         m_context.register_plugin(mk_theory_dl(m_manager)); | ||||
|     } | ||||
|  | @ -916,6 +929,11 @@ namespace smt { | |||
|         m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params)); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_CSP() { | ||||
|         setup_unknown(); | ||||
|         m_context.register_plugin(alloc(smt::theory_jobscheduler, m_manager)); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_unknown() { | ||||
|         static_features st(m_manager); | ||||
|         ptr_vector<expr> fmls; | ||||
|  | @ -926,6 +944,7 @@ namespace smt { | |||
|         setup_arrays(); | ||||
|         setup_bv(); | ||||
|         setup_datatypes(); | ||||
|         setup_recfuns(); | ||||
|         setup_dl(); | ||||
|         setup_seq_str(st); | ||||
|         setup_card(); | ||||
|  | @ -945,6 +964,7 @@ namespace smt { | |||
|             setup_seq_str(st); | ||||
|             setup_card(); | ||||
|             setup_fpa(); | ||||
|             setup_recfuns(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -81,6 +81,7 @@ namespace smt { | |||
|         void setup_QF_FPBV(); | ||||
|         void setup_QF_S(); | ||||
|         void setup_LRA(); | ||||
|         void setup_CSP(); | ||||
|         void setup_AUFLIA(bool simple_array = true); | ||||
|         void setup_AUFLIA(static_features const & st); | ||||
|         void setup_AUFLIRA(bool simple_array = true); | ||||
|  | @ -93,6 +94,7 @@ namespace smt { | |||
|         void setup_unknown(static_features & st); | ||||
|         void setup_arrays(); | ||||
|         void setup_datatypes(); | ||||
|         void setup_recfuns(); | ||||
|         void setup_bv(); | ||||
|         void setup_arith(); | ||||
|         void setup_dl(); | ||||
|  |  | |||
|  | @ -194,6 +194,15 @@ namespace smt { | |||
|             return l_false; | ||||
|         } | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief This method is called from the smt_context when an unsat core is generated. | ||||
|            The theory may tell the solver to perform iterative deepening by invalidating | ||||
|            this unsat core and increasing some resource constraints. | ||||
|         */ | ||||
|         virtual bool should_research(expr_ref_vector & unsat_core) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief This method is invoked before the search starts. | ||||
|         */ | ||||
|  | @ -294,6 +303,8 @@ namespace smt { | |||
|             SASSERT(m_context); | ||||
|             return *m_context; | ||||
|         } | ||||
| 
 | ||||
|         context & ctx() const { return get_context(); } | ||||
|          | ||||
|         ast_manager & get_manager() const { | ||||
|             SASSERT(m_manager); | ||||
|  |  | |||
|  | @ -3302,7 +3302,6 @@ namespace smt { | |||
|         return b && to_expr(b->get_value(), is_int(v), r); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     template<typename Ext> | ||||
|     bool theory_arith<Ext>::get_lower(enode * n, rational& r, bool& is_strict) { | ||||
|         theory_var v = n->get_th_var(get_id()); | ||||
|  |  | |||
							
								
								
									
										1070
									
								
								src/smt/theory_jobscheduler.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1070
									
								
								src/smt/theory_jobscheduler.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										244
									
								
								src/smt/theory_jobscheduler.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								src/smt/theory_jobscheduler.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,244 @@ | |||
| /*++
 | ||||
| Copyright (c) 2018 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     theory_jobscheduling.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Propagation solver for jobscheduling problems. | ||||
|     It relies on an external module to tighten bounds of  | ||||
|     job variables. | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2018-09-08. | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| #pragma once; | ||||
| 
 | ||||
| #include "util/uint_set.h" | ||||
| #include "ast/csp_decl_plugin.h" | ||||
| #include "ast/arith_decl_plugin.h" | ||||
| #include "smt/smt_theory.h" | ||||
| 
 | ||||
| namespace smt { | ||||
| 
 | ||||
|     typedef uint64_t time_t; | ||||
|      | ||||
|     class theory_jobscheduler : public theory { | ||||
|     public: | ||||
|         typedef svector<symbol> properties; | ||||
|     protected: | ||||
| 
 | ||||
|         struct job_resource { | ||||
|             unsigned m_resource_id;   // id of resource
 | ||||
|             time_t   m_capacity;      // amount of resource to use
 | ||||
|             unsigned m_loadpct;       // assuming loadpct
 | ||||
|             time_t   m_end;           // must run before
 | ||||
|             properties m_properties; | ||||
|             job_resource(unsigned r, time_t cap, unsigned loadpct, time_t end, properties const& ps): | ||||
|                 m_resource_id(r), m_capacity(cap), m_loadpct(loadpct), m_end(end), m_properties(ps) {} | ||||
|         }; | ||||
| 
 | ||||
|         struct job_time { | ||||
|             unsigned m_job; | ||||
|             time_t   m_time; | ||||
|             job_time(unsigned j, time_t time): m_job(j), m_time(time) {} | ||||
| 
 | ||||
|             struct compare { | ||||
|                 bool operator()(job_time const& jt1, job_time const& jt2) const { | ||||
|                     return jt1.m_time < jt2.m_time; | ||||
|                 } | ||||
|             }; | ||||
|         }; | ||||
| 
 | ||||
|         struct job_info { | ||||
|             bool                 m_is_preemptable; // can job be pre-empted
 | ||||
|             vector<job_resource> m_resources; // resources allowed to run job.
 | ||||
|             u_map<unsigned>      m_resource2index; // resource to index into vector 
 | ||||
|             enode*               m_job; | ||||
|             enode*               m_start; | ||||
|             enode*               m_end; | ||||
|             enode*               m_job2resource; | ||||
|             bool                 m_is_bound; | ||||
|             job_info(): m_is_preemptable(false), m_job(nullptr), m_start(nullptr), m_end(nullptr), m_job2resource(nullptr), m_is_bound(false)  {} | ||||
|         }; | ||||
| 
 | ||||
|         struct res_available { | ||||
|             unsigned   m_loadpct; | ||||
|             time_t     m_start; | ||||
|             time_t     m_end; | ||||
|             properties m_properties; | ||||
|             res_available(unsigned load_pct, time_t start, time_t end, properties const& ps): | ||||
|                 m_loadpct(load_pct), | ||||
|                 m_start(start), | ||||
|                 m_end(end), | ||||
|                 m_properties(ps) | ||||
|             {} | ||||
|             struct compare { | ||||
|                 bool operator()(res_available const& ra1, res_available const& ra2) const { | ||||
|                     return ra1.m_start < ra2.m_start; | ||||
|                 } | ||||
|             }; | ||||
|         }; | ||||
| 
 | ||||
|         struct res_info { | ||||
|             unsigned_vector       m_jobs;      // jobs allocated to run on resource
 | ||||
|             vector<res_available> m_available; // time intervals where resource is available
 | ||||
|             time_t                m_end;       // can't run after
 | ||||
|             enode*                m_resource; | ||||
|             enode*                m_makespan; | ||||
|             res_info(): m_end(std::numeric_limits<time_t>::max()), m_resource(nullptr), m_makespan(nullptr) {} | ||||
|         }; | ||||
|          | ||||
|         ast_manager&     m; | ||||
|         csp_util         u; | ||||
|         arith_util       a; | ||||
|         vector<job_info> m_jobs; | ||||
|         vector<res_info> m_resources; | ||||
|         unsigned_vector  m_bound_jobs; | ||||
|         unsigned         m_bound_qhead; | ||||
|         struct scope { | ||||
|             unsigned m_bound_jobs_lim; | ||||
|             unsigned m_bound_qhead; | ||||
|         }; | ||||
|         svector<scope> m_scopes; | ||||
|          | ||||
|     protected: | ||||
| 
 | ||||
|         theory_var mk_var(enode * n) override;         | ||||
| 
 | ||||
|         bool internalize_atom(app * atom, bool gate_ctx) override; | ||||
| 
 | ||||
|         bool internalize_term(app * term) override; | ||||
| 
 | ||||
|         void assign_eh(bool_var v, bool is_true) override {} | ||||
| 
 | ||||
|         void new_eq_eh(theory_var v1, theory_var v2) override; | ||||
| 
 | ||||
|         void new_diseq_eh(theory_var v1, theory_var v2) override; | ||||
| 
 | ||||
|         void push_scope_eh() override; | ||||
| 
 | ||||
|         void pop_scope_eh(unsigned num_scopes) override; | ||||
| 
 | ||||
|         final_check_status final_check_eh() override; | ||||
| 
 | ||||
|         bool can_propagate() override; | ||||
| 
 | ||||
|         void propagate() override; | ||||
|          | ||||
|     public: | ||||
| 
 | ||||
|         theory_jobscheduler(ast_manager& m); | ||||
| 
 | ||||
|         ~theory_jobscheduler() override {} | ||||
|          | ||||
|         void display(std::ostream & out) const override; | ||||
|          | ||||
|         void collect_statistics(::statistics & st) const override; | ||||
| 
 | ||||
|         bool include_func_interp(func_decl* f) override; | ||||
| 
 | ||||
|         void init_model(model_generator & m) override; | ||||
| 
 | ||||
|         model_value_proc * mk_value(enode * n, model_generator & mg) override; | ||||
| 
 | ||||
|         bool get_value(enode * n, expr_ref & r) override; | ||||
| 
 | ||||
|         theory * mk_fresh(context * new_ctx) override;  | ||||
| 
 | ||||
|     public: | ||||
|         // set up job/resource global constraints
 | ||||
|         void set_preemptable(unsigned j, bool is_preemptable); | ||||
|         void add_job_resource(unsigned j, unsigned r, unsigned loadpct, time_t cap, time_t end, properties const& ps); | ||||
|         void add_resource_available(unsigned r, unsigned max_loadpct, time_t start, time_t end, properties const& ps); | ||||
|         void add_done(); | ||||
| 
 | ||||
|         // assignments
 | ||||
|         time_t est(unsigned j);      // earliest start time of job j
 | ||||
|         time_t lst(unsigned j);      // last start time
 | ||||
|         time_t ect(unsigned j);      // earliest completion time
 | ||||
|         time_t lct(unsigned j);      // last completion time
 | ||||
|         time_t start(unsigned j);    // start time of job j
 | ||||
|         time_t end(unsigned j);      // end time of job j
 | ||||
|         time_t get_lo(expr* e); | ||||
|         time_t get_up(expr* e); | ||||
|         time_t get_value(expr* e); | ||||
|         unsigned resource(unsigned j); // resource of job j
 | ||||
| 
 | ||||
|         // derived bounds
 | ||||
|         time_t ect(unsigned j, unsigned r, time_t start); | ||||
|         bool lst(unsigned j, unsigned r, time_t& t); | ||||
|          | ||||
|         bool resource_available(job_resource const& jr, res_available const& ra) const; | ||||
|         bool first_available(job_resource const& jr, res_info const& ri, unsigned& idx) const; | ||||
|         bool last_available(job_resource const& jr, res_info const& ri, unsigned& idx) const; | ||||
| 
 | ||||
|         time_t solve_for_start(unsigned load_pct, unsigned job_load_pct, time_t end, time_t cap); | ||||
|         time_t solve_for_end(unsigned load_pct, unsigned job_load_pct, time_t start, time_t cap); | ||||
|         time_t solve_for_capacity(unsigned load_pct, unsigned job_load_pct, time_t start, time_t end); | ||||
| 
 | ||||
|         // validate assignment
 | ||||
|         void validate_assignment(); | ||||
|         bool resource_available(unsigned r, time_t t, unsigned& idx); // load available on resource r at time t.
 | ||||
|         time_t capacity_used(unsigned j, unsigned r, time_t start, time_t end);        // capacity used between start and end
 | ||||
| 
 | ||||
|         job_resource const& get_job_resource(unsigned j, unsigned r) const; | ||||
| 
 | ||||
|         // propagation
 | ||||
|         void propagate_end_time(unsigned j, unsigned r); | ||||
|         void propagate_job2resource(unsigned j, unsigned r); | ||||
| 
 | ||||
|         // final check constraints
 | ||||
|         bool constrain_end_time_interval(unsigned j, unsigned r); | ||||
|         bool constrain_resource_energy(unsigned r); | ||||
|         bool split_job2resource(unsigned j); | ||||
| 
 | ||||
|         void assert_last_end_time(unsigned j, unsigned r, job_resource const& jr, literal eq); | ||||
|         void assert_last_start_time(unsigned j, unsigned r, literal eq); | ||||
|         void assert_first_start_time(unsigned j, unsigned r, literal eq); | ||||
|         void assert_job_not_in_gap(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq); | ||||
|         void assert_job_non_preemptable(unsigned j, unsigned r, unsigned idx, unsigned idx1, literal eq); | ||||
| 
 | ||||
|         void block_job_overlap(unsigned r, uint_set const& jobs, unsigned last_job); | ||||
| 
 | ||||
|         class job_overlap { | ||||
|             time_t m_start; | ||||
|             vector<job_time> & m_starts, &m_ends; | ||||
|             unsigned s_idx, e_idx; // index into starts/ends
 | ||||
|             uint_set m_jobs; | ||||
|         public: | ||||
|             job_overlap(vector<job_time>& starts, vector<job_time>& ends); | ||||
|             bool next(); | ||||
|             uint_set const& jobs() const { return m_jobs; } | ||||
|         }; | ||||
| 
 | ||||
|         // term builders
 | ||||
|         literal mk_ge(expr* e, time_t t); | ||||
|         expr* mk_ge_expr(expr* e, time_t t); | ||||
|         literal mk_ge(enode* e, time_t t); | ||||
|         literal mk_le(expr* e, time_t t); | ||||
|         expr* mk_le_expr(expr* e, time_t t); | ||||
|         literal mk_le(enode* e, time_t t); | ||||
|         literal mk_le(enode* l, enode* r); | ||||
|         literal mk_literal(expr* e); | ||||
|         literal mk_eq_lit(enode * l, enode * r) { return mk_eq_lit(l->get_owner(), r->get_owner()); } | ||||
|         literal mk_eq_lit(expr * l, expr * r); | ||||
| 
 | ||||
|         void internalize_cmd(expr* cmd); | ||||
|         void unrecognized_argument(expr* arg) { invalid_argument("unrecognized argument ", arg); } | ||||
|         void invalid_argument(char const* msg, expr* arg); | ||||
| 
 | ||||
|         std::ostream& display(std::ostream & out, res_info const& r) const; | ||||
|         std::ostream& display(std::ostream & out, res_available const& r) const; | ||||
|         std::ostream& display(std::ostream & out, job_info const& r) const; | ||||
|         std::ostream& display(std::ostream & out, job_resource const& r) const; | ||||
| 
 | ||||
|     };     | ||||
| }; | ||||
| 
 | ||||
							
								
								
									
										451
									
								
								src/smt/theory_recfun.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								src/smt/theory_recfun.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,451 @@ | |||
| /*++
 | ||||
| Copyright (c) 2018 Microsoft Corporation, Simon Cuares | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     theory_recfun.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Theory responsible for unrolling recursive functions | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Simon Cuares December 2017 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "util/stats.h" | ||||
| #include "ast/ast_util.h" | ||||
| #include "ast/for_each_expr.h" | ||||
| #include "smt/theory_recfun.h" | ||||
| #include "smt/params/smt_params_helper.hpp" | ||||
| 
 | ||||
| 
 | ||||
| #define TRACEFN(x) TRACE("recfun", tout << x << '\n';) | ||||
| 
 | ||||
| namespace smt { | ||||
| 
 | ||||
|     theory_recfun::theory_recfun(ast_manager & m) | ||||
|         : theory(m.mk_family_id("recfun")),  | ||||
|           m(m), | ||||
|           m_plugin(*reinterpret_cast<recfun::decl::plugin*>(m.get_plugin(get_family_id()))), | ||||
|           m_util(m_plugin.u()),  | ||||
|           m_preds(m), | ||||
|           m_max_depth(0), | ||||
|           m_q_case_expand(),  | ||||
|           m_q_body_expand() | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|     theory_recfun::~theory_recfun() { | ||||
|         reset_queues(); | ||||
|     } | ||||
| 
 | ||||
|     char const * theory_recfun::get_name() const { return "recfun"; } | ||||
| 
 | ||||
|     theory* theory_recfun::mk_fresh(context* new_ctx) { | ||||
|         return alloc(theory_recfun, new_ctx->get_manager()); | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::init(context* ctx) { | ||||
|         theory::init(ctx); | ||||
|         smt_params_helper p(ctx->get_params()); | ||||
|         m_max_depth = p.recfun_depth(); | ||||
|         if (m_max_depth < 2) m_max_depth = 2; | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::init_search_eh() { | ||||
|     } | ||||
| 
 | ||||
|     bool theory_recfun::internalize_atom(app * atom, bool gate_ctx) { | ||||
|         TRACEFN(mk_pp(atom, m)); | ||||
|         if (!u().has_defs()) { | ||||
|             return false; | ||||
|         } | ||||
|         for (expr * arg : *atom) { | ||||
|             ctx().internalize(arg, false); | ||||
|         } | ||||
|         if (!ctx().e_internalized(atom)) { | ||||
|             ctx().mk_enode(atom, false, true, false); | ||||
|         } | ||||
|         if (!ctx().b_internalized(atom)) { | ||||
|             bool_var v = ctx().mk_bool_var(atom); | ||||
|             ctx().set_var_theory(v, get_id()); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool theory_recfun::internalize_term(app * term) { | ||||
|         if (!u().has_defs()) { | ||||
|             return false; | ||||
|         } | ||||
|         for (expr* e : *term) { | ||||
|             ctx().internalize(e, false); | ||||
|         } | ||||
|         // the internalization of the arguments may have triggered the internalization of term.
 | ||||
|         if (!ctx().e_internalized(term)) {             | ||||
|             ctx().mk_enode(term, false, false, true); | ||||
|         } | ||||
|         return true;  | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::reset_queues() { | ||||
|         for (auto* e : m_q_case_expand) { | ||||
|             dealloc(e); | ||||
|         } | ||||
|         m_q_case_expand.reset(); | ||||
|         for (auto* e : m_q_body_expand) { | ||||
|             dealloc(e); | ||||
|         } | ||||
|         m_q_body_expand.reset(); | ||||
|         m_q_clauses.clear(); | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::reset_eh() { | ||||
|         reset_queues();    | ||||
|         m_stats.reset(); | ||||
|         theory::reset_eh(); | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * when `n` becomes relevant, if it's `f(t1...tn)` with `f` defined, | ||||
|      * then case-expand `n`. If it's a macro we can also immediately | ||||
|      * body-expand it. | ||||
|      */ | ||||
|     void theory_recfun::relevant_eh(app * n) { | ||||
|         SASSERT(ctx().relevancy()); | ||||
|         if (u().is_defined(n) && u().has_defs()) { | ||||
|             TRACEFN("relevant_eh: (defined) " <<  mk_pp(n, m));         | ||||
|             push_case_expand(alloc(case_expansion, u(), n)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::push_scope_eh() { | ||||
|         theory::push_scope_eh(); | ||||
|         m_preds_lim.push_back(m_preds.size()); | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::pop_scope_eh(unsigned num_scopes) { | ||||
|         theory::pop_scope_eh(num_scopes); | ||||
|         reset_queues(); | ||||
|          | ||||
|         // restore depth book-keeping
 | ||||
|         unsigned new_lim = m_preds_lim.size()-num_scopes; | ||||
| #if 0 | ||||
|         // depth tracking of recursive unfolding is 
 | ||||
|         // turned off when enabling this code:
 | ||||
|         unsigned start = m_preds_lim[new_lim]; | ||||
|         for (unsigned i = start; i < m_preds.size(); ++i) { | ||||
|             m_pred_depth.remove(m_preds.get(i)); | ||||
|         } | ||||
|         m_preds.resize(start); | ||||
| #endif | ||||
|         m_preds_lim.shrink(new_lim); | ||||
|     } | ||||
|      | ||||
|     void theory_recfun::restart_eh() { | ||||
|         TRACEFN("restart"); | ||||
|         reset_queues(); | ||||
|         theory::restart_eh(); | ||||
|     } | ||||
|       | ||||
|     bool theory_recfun::can_propagate() { | ||||
|         return ! (m_q_case_expand.empty() && | ||||
|                   m_q_body_expand.empty() && | ||||
|                   m_q_clauses.empty()); | ||||
|     } | ||||
|      | ||||
|     void theory_recfun::propagate() { | ||||
|   | ||||
|         for (literal_vector & c : m_q_clauses) { | ||||
|             TRACEFN("add axiom " << pp_lits(ctx(), c)); | ||||
|             ctx().mk_th_axiom(get_id(), c); | ||||
|         } | ||||
|         m_q_clauses.clear(); | ||||
| 		 | ||||
|         for (unsigned i = 0; i < m_q_case_expand.size(); ++i) { | ||||
|             case_expansion* e = m_q_case_expand[i]; | ||||
|             if (e->m_def->is_fun_macro()) { | ||||
|                 // body expand immediately
 | ||||
|                 assert_macro_axiom(*e); | ||||
|             } | ||||
|             else { | ||||
|                 // case expand
 | ||||
|                 SASSERT(e->m_def->is_fun_defined()); | ||||
|                 assert_case_axioms(*e);                 | ||||
|             } | ||||
|             dealloc(e); | ||||
|             m_q_case_expand[i] = nullptr; | ||||
|         } | ||||
|         m_stats.m_case_expansions += m_q_case_expand.size(); | ||||
|         m_q_case_expand.reset(); | ||||
| 
 | ||||
|         for (unsigned i = 0; i < m_q_body_expand.size(); ++i) { | ||||
|             assert_body_axiom(*m_q_body_expand[i]); | ||||
|             dealloc(m_q_body_expand[i]); | ||||
|             m_q_body_expand[i] = nullptr; | ||||
|         } | ||||
|         m_stats.m_body_expansions += m_q_body_expand.size(); | ||||
|         m_q_body_expand.reset(); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * make clause `depth_limit => ~guard` | ||||
|      * the guard appears at a depth below the current cutoff. | ||||
|      */ | ||||
|     void theory_recfun::assert_max_depth_limit(expr* guard) { | ||||
|         literal_vector c; | ||||
|         app_ref dlimit = m_util.mk_depth_limit_pred(m_max_depth); | ||||
|         c.push_back(~mk_literal(dlimit)); | ||||
|         c.push_back(~mk_literal(guard));         | ||||
|         TRACEFN("max-depth limit: add clause " << pp_lits(ctx(), c)); | ||||
|         m_q_clauses.push_back(std::move(c)); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * retrieve depth associated with predicate or expression. | ||||
|      */ | ||||
|     unsigned theory_recfun::get_depth(expr* e) { | ||||
|         SASSERT(u().is_defined(e) || u().is_case_pred(e)); | ||||
|         unsigned d = 0; | ||||
|         m_pred_depth.find(e, d); | ||||
|         TRACEFN("depth " << d << " " << mk_pp(e, m)); | ||||
|         return d; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Update depth of subterms of e with respect to d. | ||||
|      */ | ||||
|     void theory_recfun::set_depth_rec(unsigned d, expr* e) { | ||||
|         struct insert_c { | ||||
|             theory_recfun& th; | ||||
|             unsigned m_depth; | ||||
|             insert_c(theory_recfun& th, unsigned d): th(th), m_depth(d) {} | ||||
|             void operator()(app* e) { th.set_depth(m_depth, e); } | ||||
|             void operator()(quantifier*) {} | ||||
|             void operator()(var*) {} | ||||
|         }; | ||||
|         insert_c proc(*this, d); | ||||
|         for_each_expr(proc, e); | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::set_depth(unsigned depth, expr* e) {         | ||||
|         if ((u().is_defined(e) || u().is_case_pred(e)) && !m_pred_depth.contains(e)) { | ||||
|             m_pred_depth.insert(e, depth); | ||||
|             m_preds.push_back(e); | ||||
|             TRACEFN("depth " << depth << " : " << mk_pp(e, m)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * if `is_true` and `v = C_f_i(t1...tn)`,  | ||||
|      *    then body-expand i-th case of `f(t1...tn)` | ||||
|      */ | ||||
|     void theory_recfun::assign_eh(bool_var v, bool is_true) { | ||||
|         expr* e = ctx().bool_var2expr(v); | ||||
|         if (is_true && u().is_case_pred(e)) { | ||||
|             TRACEFN("assign_case_pred_true " << mk_pp(e, m)); | ||||
|             // body-expand
 | ||||
|             push_body_expand(alloc(body_expansion, u(), to_app(e)));           | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|      // replace `vars` by `args` in `e`
 | ||||
|     expr_ref theory_recfun::apply_args( | ||||
|         unsigned depth, | ||||
|         recfun::vars const & vars, | ||||
|         ptr_vector<expr> const & args, | ||||
|         expr * e) { | ||||
|         SASSERT(is_standard_order(vars)); | ||||
|         var_subst subst(m, true); | ||||
|         expr_ref new_body(m); | ||||
|         new_body = subst(e, args.size(), args.c_ptr()); | ||||
|         ctx().get_rewriter()(new_body); // simplify
 | ||||
|         set_depth_rec(depth + 1, new_body); | ||||
|         return new_body; | ||||
|     } | ||||
|          | ||||
|     literal theory_recfun::mk_literal(expr* e) { | ||||
|         ctx().internalize(e, false); | ||||
|         literal lit = ctx().get_literal(e); | ||||
|         ctx().mark_as_relevant(lit); | ||||
|         return lit; | ||||
|     } | ||||
| 
 | ||||
|     literal theory_recfun::mk_eq_lit(expr* l, expr* r) { | ||||
|         literal lit; | ||||
|         if (m.is_true(r) || m.is_false(r)) { | ||||
|             std::swap(l, r); | ||||
|         } | ||||
|         if (m.is_true(l)) { | ||||
|             lit = mk_literal(r); | ||||
|         } | ||||
|         else if (m.is_false(l)) { | ||||
|             lit = ~mk_literal(r); | ||||
|         } | ||||
|         else { | ||||
|             lit = mk_eq(l, r, false);         | ||||
|         } | ||||
|         ctx().mark_as_relevant(lit); | ||||
|         return lit; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * For functions f(args) that are given as macros f(vs) = rhs | ||||
|      *  | ||||
|      * 1. substitute `e.args` for `vs` into the macro rhs | ||||
|      * 2. add unit clause `f(args) = rhs` | ||||
|      */ | ||||
|     void theory_recfun::assert_macro_axiom(case_expansion & e) { | ||||
|         m_stats.m_macro_expansions++; | ||||
|         TRACEFN("case expansion         " << pp_case_expansion(e, m) << "\n"); | ||||
|         SASSERT(e.m_def->is_fun_macro()); | ||||
|         auto & vars = e.m_def->get_vars(); | ||||
|         expr_ref lhs(e.m_lhs, m); | ||||
|         unsigned depth = get_depth(e.m_lhs); | ||||
|         expr_ref rhs(apply_args(depth, vars, e.m_args, e.m_def->get_rhs()), m); | ||||
|         literal lit = mk_eq_lit(lhs, rhs); | ||||
|         ctx().mk_th_axiom(get_id(), 1, &lit); | ||||
|         TRACEFN("macro expansion yields " << mk_pp(rhs, m) << "\n" << | ||||
|                 "literal                " << pp_lit(ctx(), lit)); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Add case axioms for every case expansion path. | ||||
|      * | ||||
|      * assert `p(args) <=> And(guards)` (with CNF on the fly) | ||||
|      * | ||||
|      * also body-expand paths that do not depend on any defined fun | ||||
|      */ | ||||
|     void theory_recfun::assert_case_axioms(case_expansion & e) { | ||||
|         TRACEFN("assert_case_axioms "<< pp_case_expansion(e,m) | ||||
|               << " with " << e.m_def->get_cases().size() << " cases"); | ||||
|         SASSERT(e.m_def->is_fun_defined()); | ||||
|         // add case-axioms for all case-paths
 | ||||
|         auto & vars = e.m_def->get_vars(); | ||||
|         literal_vector preds; | ||||
|         for (recfun::case_def const & c : e.m_def->get_cases()) { | ||||
|             // applied predicate to `args`
 | ||||
|             app_ref pred_applied = c.apply_case_predicate(e.m_args); | ||||
| 
 | ||||
|             // cut off cases below max-depth
 | ||||
|             unsigned depth = get_depth(e.m_lhs); | ||||
|             set_depth(depth, pred_applied); | ||||
|             SASSERT(u().owns_app(pred_applied)); | ||||
|             literal concl = mk_literal(pred_applied); | ||||
|             preds.push_back(concl); | ||||
| 
 | ||||
|             if (depth >= m_max_depth) { | ||||
|                 assert_max_depth_limit(pred_applied); | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             literal_vector guards; | ||||
|             guards.push_back(concl); | ||||
|             for (auto & g : c.get_guards()) { | ||||
|                 expr_ref ga = apply_args(depth, vars, e.m_args, g); | ||||
|                 literal guard = mk_literal(ga); | ||||
|                 guards.push_back(~guard); | ||||
|                 literal c[2] = {~concl, guard}; | ||||
|                 ctx().mk_th_axiom(get_id(), 2, c); | ||||
|             } | ||||
|             ctx().mk_th_axiom(get_id(), guards); | ||||
| 
 | ||||
|             if (c.is_immediate()) { | ||||
|                 body_expansion be(pred_applied, c, e.m_args); | ||||
|                 assert_body_axiom(be); | ||||
|             } | ||||
|         } | ||||
|         // the disjunction of branches is asserted
 | ||||
|         // to close the available cases. 
 | ||||
|         ctx().mk_th_axiom(get_id(), preds); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * For a guarded definition guards => f(vars) = rhs | ||||
|      * and occurrence f(args) | ||||
|      * | ||||
|      * substitute `args` for `vars` in guards, and rhs | ||||
|      * add axiom guards[args/vars] => f(args) = rhs[args/vars] | ||||
|      * | ||||
|      */ | ||||
|     void theory_recfun::assert_body_axiom(body_expansion & e) { | ||||
|         recfun::def & d = *e.m_cdef->get_def(); | ||||
|         auto & vars = d.get_vars(); | ||||
|         auto & args = e.m_args; | ||||
|         SASSERT(is_standard_order(vars)); | ||||
|         unsigned depth = get_depth(e.m_pred); | ||||
|         expr_ref lhs(u().mk_fun_defined(d, args), m); | ||||
|         expr_ref rhs = apply_args(depth, vars, args, e.m_cdef->get_rhs()); | ||||
| 
 | ||||
|         literal_vector clause; | ||||
|         for (auto & g : e.m_cdef->get_guards()) { | ||||
|             expr_ref guard = apply_args(depth, vars, args, g); | ||||
|             clause.push_back(~mk_literal(guard)); | ||||
|             if (clause.back() == true_literal) { | ||||
|                 return; | ||||
|             } | ||||
|             if (clause.back() == false_literal) { | ||||
|                 clause.pop_back(); | ||||
|             } | ||||
|         }         | ||||
|         clause.push_back(mk_eq_lit(lhs, rhs)); | ||||
|         ctx().mk_th_axiom(get_id(), clause); | ||||
|         TRACEFN("body   " << pp_body_expansion(e,m)); | ||||
|         TRACEFN(pp_lits(ctx(), clause)); | ||||
|     } | ||||
|      | ||||
|     final_check_status theory_recfun::final_check_eh() { | ||||
|         TRACEFN("final\n"); | ||||
|         if (can_propagate()) { | ||||
|             propagate(); | ||||
|             return FC_CONTINUE; | ||||
|         } | ||||
|         return FC_DONE; | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::add_theory_assumptions(expr_ref_vector & assumptions) { | ||||
|         if (u().has_defs()) { | ||||
|             app_ref dlimit = m_util.mk_depth_limit_pred(m_max_depth); | ||||
|             TRACEFN("add_theory_assumption " << mk_pp(dlimit.get(), m)); | ||||
|             assumptions.push_back(dlimit); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // if `dlimit` occurs in unsat core, return 'true'
 | ||||
|     bool theory_recfun::should_research(expr_ref_vector & unsat_core) { | ||||
|         for (auto & e : unsat_core) { | ||||
|             if (u().is_depth_limit(e)) { | ||||
|                 m_max_depth = (3 * m_max_depth) / 2; | ||||
|                 IF_VERBOSE(1, verbose_stream() << "(smt.recfun :increase-depth " << m_max_depth << ")\n"); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::display(std::ostream & out) const { | ||||
|         out << "recfun{}"; | ||||
|     } | ||||
| 
 | ||||
|     void theory_recfun::collect_statistics(::statistics & st) const { | ||||
|         st.update("recfun macro expansion", m_stats.m_macro_expansions); | ||||
|         st.update("recfun case expansion", m_stats.m_case_expansions); | ||||
|         st.update("recfun body expansion", m_stats.m_body_expansions); | ||||
|     } | ||||
| 
 | ||||
|     std::ostream& operator<<(std::ostream & out, theory_recfun::pp_case_expansion const & e) { | ||||
|         return out << "case_exp(" << mk_pp(e.e.m_lhs, e.m) << ")"; | ||||
|     } | ||||
| 
 | ||||
|     std::ostream& operator<<(std::ostream & out, theory_recfun::pp_body_expansion const & e) { | ||||
|         out << "body_exp(" << e.e.m_cdef->get_decl()->get_name(); | ||||
|         for (auto* t : e.e.m_args) { | ||||
|             out << " " << mk_pp(t,e.m); | ||||
|         } | ||||
|         return out << ")"; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										163
									
								
								src/smt/theory_recfun.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/smt/theory_recfun.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,163 @@ | |||
| /*++
 | ||||
| Copyright (c) 2018 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     theory_recfun.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Theory responsible for unrolling recursive functions | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Simon Cuares December 2017 | ||||
| 
 | ||||
| Revision History: | ||||
| 
 | ||||
| --*/ | ||||
| #ifndef THEORY_RECFUN_H_ | ||||
| #define THEORY_RECFUN_H_ | ||||
| 
 | ||||
| #include "smt/smt_theory.h" | ||||
| #include "smt/smt_context.h" | ||||
| #include "ast/ast_pp.h" | ||||
| #include "ast/recfun_decl_plugin.h" | ||||
| 
 | ||||
| namespace smt { | ||||
| 
 | ||||
|     class theory_recfun : public theory { | ||||
|         struct stats { | ||||
|             unsigned m_case_expansions, m_body_expansions, m_macro_expansions; | ||||
|             void reset() { memset(this, 0, sizeof(stats)); } | ||||
|             stats() { reset(); } | ||||
|         }; | ||||
| 
 | ||||
|         // one case-expansion of `f(t1...tn)`
 | ||||
|         struct case_expansion { | ||||
|             app *              m_lhs; // the term to expand
 | ||||
|             recfun::def *       m_def; | ||||
|             ptr_vector<expr>    m_args; | ||||
|             case_expansion(recfun::util& u, app * n) :  | ||||
|             m_lhs(n), m_def(nullptr), m_args()  { | ||||
|                 SASSERT(u.is_defined(n)); | ||||
|                 func_decl * d = n->get_decl(); | ||||
|                 m_def = &u.get_def(d); | ||||
|                 m_args.append(n->get_num_args(), n->get_args()); | ||||
|             } | ||||
|             case_expansion(case_expansion const & from) | ||||
|                 : m_lhs(from.m_lhs), | ||||
|                   m_def(from.m_def), | ||||
|                   m_args(from.m_args) {} | ||||
|             case_expansion(case_expansion && from) | ||||
|                 : m_lhs(from.m_lhs), | ||||
|                   m_def(from.m_def), | ||||
|                   m_args(std::move(from.m_args)) {} | ||||
|         }; | ||||
| 
 | ||||
|         struct pp_case_expansion { | ||||
|             case_expansion & e; | ||||
|             ast_manager & m; | ||||
|             pp_case_expansion(case_expansion & e, ast_manager & m) : e(e), m(m) {} | ||||
|         }; | ||||
| 
 | ||||
|         friend std::ostream& operator<<(std::ostream&, pp_case_expansion const &); | ||||
| 
 | ||||
|         // one body-expansion of `f(t1...tn)` using a `C_f_i(t1...tn)`
 | ||||
|         struct body_expansion { | ||||
|             app*                    m_pred; | ||||
|             recfun::case_def const * m_cdef; | ||||
|             ptr_vector<expr>        m_args; | ||||
| 
 | ||||
|             body_expansion(recfun::util& u, app * n) : m_pred(n), m_cdef(0), m_args() { | ||||
|                 m_cdef = &u.get_case_def(n); | ||||
|                 m_args.append(n->get_num_args(), n->get_args()); | ||||
|             } | ||||
|             body_expansion(app* pred, recfun::case_def const & d, ptr_vector<expr> & args) :  | ||||
|                 m_pred(pred), m_cdef(&d), m_args(args) {} | ||||
|             body_expansion(body_expansion const & from):  | ||||
|                 m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(from.m_args) {} | ||||
|             body_expansion(body_expansion && from) :  | ||||
|                 m_pred(from.m_pred), m_cdef(from.m_cdef), m_args(std::move(from.m_args)) {} | ||||
|         }; | ||||
| 
 | ||||
|         struct pp_body_expansion { | ||||
|             body_expansion & e; | ||||
|             ast_manager & m; | ||||
|             pp_body_expansion(body_expansion & e, ast_manager & m) : e(e), m(m) {} | ||||
|         }; | ||||
| 
 | ||||
|         friend std::ostream& operator<<(std::ostream&, pp_body_expansion const &); | ||||
|          | ||||
|         ast_manager&            m; | ||||
|         recfun::decl::plugin&   m_plugin; | ||||
|         recfun::util&           m_util; | ||||
|         stats                   m_stats; | ||||
| 
 | ||||
|         // book-keeping for depth of predicates
 | ||||
|         obj_map<expr, unsigned>  m_pred_depth; | ||||
|         expr_ref_vector          m_preds; | ||||
|         unsigned_vector          m_preds_lim; | ||||
|         unsigned                 m_max_depth; // for fairness and termination
 | ||||
| 
 | ||||
|         ptr_vector<case_expansion> m_q_case_expand; | ||||
|         ptr_vector<body_expansion> m_q_body_expand; | ||||
|         vector<literal_vector> m_q_clauses; | ||||
| 
 | ||||
|         recfun::util & u() const { return m_util; } | ||||
|         bool is_defined(app * f) const { return u().is_defined(f); } | ||||
|         bool is_case_pred(app * f) const { return u().is_case_pred(f); } | ||||
| 
 | ||||
|         bool is_defined(enode * e) const { return is_defined(e->get_owner()); } | ||||
|         bool is_case_pred(enode * e) const { return is_case_pred(e->get_owner()); } | ||||
| 
 | ||||
|         void reset_queues(); | ||||
|         expr_ref apply_args(unsigned depth, recfun::vars const & vars, ptr_vector<expr> const & args, expr * e); //!< substitute variables by args
 | ||||
|         void assert_macro_axiom(case_expansion & e); | ||||
|         void assert_case_axioms(case_expansion & e); | ||||
|         void assert_body_axiom(body_expansion & e); | ||||
|         literal mk_literal(expr* e); | ||||
| 
 | ||||
|         void assert_max_depth_limit(expr* guard); | ||||
|         unsigned get_depth(expr* e); | ||||
|         void set_depth(unsigned d, expr* e); | ||||
|         void set_depth_rec(unsigned d, expr* e); | ||||
|          | ||||
|         literal mk_eq_lit(expr* l, expr* r); | ||||
|         bool is_standard_order(recfun::vars const& vars) const {  | ||||
|             return vars.size() == 0 || vars[vars.size()-1]->get_idx() == 0;  | ||||
|         } | ||||
|     protected: | ||||
|         void push_case_expand(case_expansion* e) { m_q_case_expand.push_back(e); } | ||||
|         void push_body_expand(body_expansion* e) { m_q_body_expand.push_back(e); } | ||||
| 
 | ||||
|         bool internalize_atom(app * atom, bool gate_ctx) override; | ||||
|         bool internalize_term(app * term) override; | ||||
|         void reset_eh() override; | ||||
|         void relevant_eh(app * n) override; | ||||
|         char const * get_name() const override; | ||||
|         final_check_status final_check_eh() override; | ||||
|         void assign_eh(bool_var v, bool is_true) override; | ||||
|         void push_scope_eh() override; | ||||
|         void pop_scope_eh(unsigned num_scopes) override; | ||||
|         void restart_eh() override; | ||||
|         bool can_propagate() override; | ||||
|         void propagate() override; | ||||
|         bool should_research(expr_ref_vector &) override; | ||||
| 
 | ||||
|         void new_eq_eh(theory_var v1, theory_var v2) override {} | ||||
|         void new_diseq_eh(theory_var v1, theory_var v2) override {} | ||||
|         void add_theory_assumptions(expr_ref_vector & assumptions) override; | ||||
|         void init(context* ctx) override; | ||||
| 
 | ||||
|     public: | ||||
|         theory_recfun(ast_manager & m); | ||||
|         ~theory_recfun() override; | ||||
|         theory * mk_fresh(context * new_ctx) override; | ||||
|         void init_search_eh() override; | ||||
|         void display(std::ostream & out) const override; | ||||
|         void collect_statistics(::statistics & st) const override; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -22,7 +22,7 @@ Revision History: | |||
| 
 | ||||
| 
 | ||||
| bool smt_logics::supported_logic(symbol const & s) { | ||||
|     return logic_has_uf(s) || logic_is_all(s) || logic_has_fd(s) || | ||||
|     return logic_has_uf(s) || logic_is_allcsp(s) || logic_has_fd(s) ||  | ||||
|         logic_has_arith(s) || logic_has_bv(s) || | ||||
|         logic_has_array(s) || logic_has_seq(s) || logic_has_str(s) || | ||||
|         logic_has_horn(s) || logic_has_fpa(s); | ||||
|  | @ -83,7 +83,7 @@ bool smt_logics::logic_has_arith(symbol const & s) { | |||
|         s == "QF_FPBV" || | ||||
|         s == "QF_BVFP" || | ||||
|         s == "QF_S" || | ||||
|         s == "ALL" || | ||||
|         logic_is_allcsp(s) || | ||||
|         s == "QF_FD" || | ||||
|         s == "HORN" || | ||||
|         s == "QF_FPLRA"; | ||||
|  | @ -102,7 +102,7 @@ bool smt_logics::logic_has_bv(symbol const & s) { | |||
|         s == "QF_BVRE" || | ||||
|         s == "QF_FPBV" || | ||||
|         s == "QF_BVFP" || | ||||
|         s == "ALL" || | ||||
|         logic_is_allcsp(s) || | ||||
|         s == "QF_FD" || | ||||
|         s == "HORN"; | ||||
| } | ||||
|  | @ -123,22 +123,22 @@ bool smt_logics::logic_has_array(symbol const & s) { | |||
|         s == "AUFNIRA" || | ||||
|         s == "AUFBV" || | ||||
|         s == "ABV" || | ||||
|         s == "ALL" || | ||||
|         logic_is_allcsp(s) || | ||||
|         s == "QF_ABV" || | ||||
|         s == "QF_AUFBV" || | ||||
|         s == "HORN"; | ||||
| } | ||||
| 
 | ||||
| bool smt_logics::logic_has_seq(symbol const & s) { | ||||
|     return s == "QF_BVRE" || s == "QF_S" || s == "ALL"; | ||||
|     return s == "QF_BVRE" || s == "QF_S" || logic_is_allcsp(s); | ||||
| } | ||||
| 
 | ||||
| bool smt_logics::logic_has_str(symbol const & s) { | ||||
|     return s == "QF_S" || s == "ALL"; | ||||
|     return s == "QF_S" || logic_is_allcsp(s); | ||||
| } | ||||
| 
 | ||||
| bool smt_logics::logic_has_fpa(symbol const & s) { | ||||
|     return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA"  || s == "ALL"; | ||||
|     return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA"  || logic_is_allcsp(s); | ||||
| } | ||||
| 
 | ||||
| bool smt_logics::logic_has_uf(symbol const & s) { | ||||
|  | @ -150,9 +150,10 @@ bool smt_logics::logic_has_horn(symbol const& s) { | |||
| } | ||||
| 
 | ||||
| bool smt_logics::logic_has_pb(symbol const& s) { | ||||
|     return s == "QF_FD" || s == "ALL" || logic_has_horn(s); | ||||
|     return s == "QF_FD" || logic_is_allcsp(s) || logic_has_horn(s); | ||||
| } | ||||
| 
 | ||||
| bool smt_logics::logic_has_datatype(symbol const& s) { | ||||
|     return s == "QF_FD" || s == "ALL" || s == "QF_DT"; | ||||
|     return s == "QF_FD" || logic_is_allcsp(s) || s == "QF_DT"; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,6 +25,8 @@ public: | |||
|     static bool supported_logic(symbol const & s); | ||||
|     static bool logic_has_reals_only(symbol const& l);        | ||||
|     static bool logic_is_all(symbol const& s) { return s == "ALL"; } | ||||
|     static bool logic_is_csp(symbol const& s) { return s == "CSP"; } | ||||
|     static bool logic_is_allcsp(symbol const& s) { return logic_is_all(s) || logic_is_csp(s); } | ||||
|     static bool logic_has_uf(symbol const& s); | ||||
|     static bool logic_has_arith(symbol const & s); | ||||
|     static bool logic_has_bv(symbol const & s); | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ public: | |||
|     void reset() { std::for_each(m_vector.begin(), m_vector.end(), delete_proc<T>()); m_vector.reset(); } | ||||
|     void push_back(T * ptr) { m_vector.push_back(ptr); } | ||||
|     void pop_back() { SASSERT(!empty()); set(size()-1, nullptr); m_vector.pop_back(); } | ||||
|     T * back() const { return m_vector.back(); } | ||||
|     T * operator[](unsigned idx) const { return m_vector[idx]; } | ||||
|     void set(unsigned idx, T * ptr) {  | ||||
|         if (m_vector[idx] == ptr)  | ||||
|  | @ -51,6 +52,13 @@ public: | |||
|                 push_back(nullptr); | ||||
|         } | ||||
|     } | ||||
|     //!< swap last element with given pointer
 | ||||
|     void swap_back(scoped_ptr<T> & ptr) { | ||||
|         SASSERT(!empty()); | ||||
|         T * tmp = ptr.detach(); | ||||
|         ptr = m_vector.back(); | ||||
|         m_vector[m_vector.size()-1] = tmp; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -143,6 +143,17 @@ public: | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| template<typename Ctx, typename M, typename Mgr, typename D> | ||||
| class insert_ref_map : public trail<Ctx> { | ||||
|     Mgr&          m; | ||||
|     M&            m_map; | ||||
|     D             m_obj; | ||||
| public: | ||||
|     insert_ref_map(Mgr& m, M& t, D o) : m(m), m_map(t), m_obj(o) {} | ||||
|     virtual ~insert_ref_map() {} | ||||
|     virtual void undo(Ctx & ctx) { m_map.remove(m_obj); m.dec_ref(m_obj); } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| template<typename Ctx, typename V> | ||||
| class push_back_vector : public trail<Ctx> { | ||||
|  |  | |||
|  | @ -269,10 +269,10 @@ public: | |||
|     void remove(unsigned v) { | ||||
|         if (contains(v)) { | ||||
|             m_in_set[v] = false; | ||||
|             unsigned i = 0; | ||||
|             for (i = 0; i < m_set.size() && m_set[i] != v; ++i) | ||||
|             unsigned i = m_set.size(); | ||||
|             for (; i > 0 && m_set[--i] != v; )  | ||||
|                 ; | ||||
|             SASSERT(i < m_set.size()); | ||||
|             SASSERT(m_set[i] == v); | ||||
|             m_set[i] = m_set.back(); | ||||
|             m_set.pop_back(); | ||||
|         } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue