mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-28 10:19:23 +00:00 
			
		
		
		
	running updates to bv_solver (#4674)
* na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * dbg Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * bv Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * drat and fresh Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * move ackerman functionality Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * debugability Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * towards debugability Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * missing file Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove csp Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									4d1a2a2784
								
							
						
					
					
						commit
						d02b0cde7a
					
				
					 63 changed files with 3060 additions and 3095 deletions
				
			
		|  | @ -14,7 +14,6 @@ z3_add_component(ast | ||||||
|     ast_translation.cpp |     ast_translation.cpp | ||||||
|     ast_util.cpp |     ast_util.cpp | ||||||
|     bv_decl_plugin.cpp |     bv_decl_plugin.cpp | ||||||
|     csp_decl_plugin.cpp |  | ||||||
|     datatype_decl_plugin.cpp |     datatype_decl_plugin.cpp | ||||||
|     decl_collector.cpp |     decl_collector.cpp | ||||||
|     display_dimacs.cpp |     display_dimacs.cpp | ||||||
|  |  | ||||||
|  | @ -1342,6 +1342,17 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { | ||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::ostream& operator<<(std::ostream& out, mk_ismt2_func const& p) { | ||||||
|  |     smt2_pp_environment_dbg env(p.m);         | ||||||
|  |     format_ref r(fm(p.m)); | ||||||
|  |     unsigned len = 0; | ||||||
|  |     r = env.pp_fdecl(p.m_fn, len); | ||||||
|  |     params_ref pa; | ||||||
|  |     pp(out, r.get(), p.m, pa); | ||||||
|  |     return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| std::ostream& operator<<(std::ostream& out, expr_ref const&  e) { | std::ostream& operator<<(std::ostream& out, expr_ref const&  e) { | ||||||
|     return out << mk_ismt2_pp(e.get(), e.get_manager()); |     return out << mk_ismt2_pp(e.get(), e.get_manager()); | ||||||
| } | } | ||||||
|  | @ -1388,6 +1399,8 @@ std::ostream& operator<<(std::ostream& out, sort_ref_vector const&  e) { | ||||||
|     return out; |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| #ifdef Z3DEBUG | #ifdef Z3DEBUG | ||||||
| void pp(expr const * n, ast_manager & m) { | void pp(expr const * n, ast_manager & m) { | ||||||
|     std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl; |     std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl; | ||||||
|  |  | ||||||
|  | @ -122,6 +122,7 @@ struct mk_ismt2_pp { | ||||||
|     mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); |     mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); | std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); | ||||||
| 
 | 
 | ||||||
| std::ostream& operator<<(std::ostream& out, expr_ref const& e); | std::ostream& operator<<(std::ostream& out, expr_ref const& e); | ||||||
|  | @ -136,3 +137,10 @@ std::ostream& operator<<(std::ostream& out, var_ref_vector const& e); | ||||||
| std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e); | std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e); | ||||||
| std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e); | std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e); | ||||||
| 
 | 
 | ||||||
|  | struct mk_ismt2_func { | ||||||
|  |     func_decl* m_fn; | ||||||
|  |     ast_manager &m; | ||||||
|  |     mk_ismt2_func(func_decl* f, ast_manager& m): m_fn(f), m(m) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | std::ostream& operator<<(std::ostream& out, mk_ismt2_func const& f); | ||||||
|  |  | ||||||
|  | @ -131,12 +131,8 @@ void bv_decl_plugin::finalize() { | ||||||
| 
 | 
 | ||||||
|     DEC_REF(m_int2bv); |     DEC_REF(m_int2bv); | ||||||
|     DEC_REF(m_bv2int); |     DEC_REF(m_bv2int); | ||||||
|     vector<ptr_vector<func_decl> >::iterator it  = m_bit2bool.begin(); |     for (auto& ds : m_bit2bool) | ||||||
|     vector<ptr_vector<func_decl> >::iterator end = m_bit2bool.end(); |  | ||||||
|     for (; it != end; ++it) { |  | ||||||
|         ptr_vector<func_decl> & ds = *it; |  | ||||||
|         DEC_REF(ds); |         DEC_REF(ds); | ||||||
|     } |  | ||||||
|     DEC_REF(m_mkbv); |     DEC_REF(m_mkbv); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -829,6 +825,14 @@ bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const { | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool bv_recognizers::is_bit2bool(expr* e, expr*& bv, unsigned& idx) const { | ||||||
|  |     if (!is_bit2bool(e)) | ||||||
|  |         return false; | ||||||
|  |     bv = to_app(e)->get_arg(0); | ||||||
|  |     idx = to_app(e)->get_parameter(0).get_int(); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool bv_recognizers::mult_inverse(rational const & n, unsigned bv_size, rational & result) { | bool bv_recognizers::mult_inverse(rational const & n, unsigned bv_size, rational & result) { | ||||||
|     if (n.is_one()) { |     if (n.is_one()) { | ||||||
|         result = n; |         result = n; | ||||||
|  |  | ||||||
|  | @ -368,6 +368,9 @@ public: | ||||||
|     MATCH_BINARY(is_bv_sdivi); |     MATCH_BINARY(is_bv_sdivi); | ||||||
|     MATCH_BINARY(is_bv_udivi); |     MATCH_BINARY(is_bv_udivi); | ||||||
|     MATCH_BINARY(is_bv_smodi); |     MATCH_BINARY(is_bv_smodi); | ||||||
|  |     MATCH_UNARY(is_bit2bool); | ||||||
|  |     MATCH_UNARY(is_int2bv); | ||||||
|  |     bool is_bit2bool(expr* e, expr*& bv, unsigned& idx) const; | ||||||
| 
 | 
 | ||||||
|     rational norm(rational const & val, unsigned bv_size, bool is_signed) const ; |     rational norm(rational const & val, unsigned bv_size, bool is_signed) const ; | ||||||
|     rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); } |     rational norm(rational const & val, unsigned bv_size) const { return norm(val, bv_size, false); } | ||||||
|  | @ -432,6 +435,7 @@ public: | ||||||
|     app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } |     app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } | ||||||
|     app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } |     app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } | ||||||
|     app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } |     app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } | ||||||
|  |     app * mk_bit2bool(expr* e, unsigned idx) { parameter p(idx); return m_manager.mk_app(get_fid(), OP_BIT2BOOL, 1, &p, 1, &e); } | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|     void log_bv_from_exprs(app * r, unsigned n, expr* const* es) { |     void log_bv_from_exprs(app * r, unsigned n, expr* const* es) { | ||||||
|  |  | ||||||
|  | @ -1,386 +0,0 @@ | ||||||
| /*++
 |  | ||||||
| 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 = nullptr; |  | ||||||
|     m_alist_sort = nullptr; |  | ||||||
|     m_job_sort = nullptr; |  | ||||||
|     m_resource_sort = nullptr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void csp_decl_plugin::init() { |  | ||||||
|     if (!m_int_sort) { |  | ||||||
|         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) { |  | ||||||
|     init(); |  | ||||||
|     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 *) { |  | ||||||
|     init(); |  | ||||||
|     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 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[1] != 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 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) { |  | ||||||
|     init(); |  | ||||||
|     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; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | @ -1,163 +0,0 @@ | ||||||
| /*++
 |  | ||||||
| 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 { |  | ||||||
|     void init(); |  | ||||||
| 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& finite_capacity_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); |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
|  | @ -17,52 +17,11 @@ Author: | ||||||
| 
 | 
 | ||||||
| #include "ast/euf/euf_egraph.h" | #include "ast/euf/euf_egraph.h" | ||||||
| #include "ast/ast_pp.h" | #include "ast/ast_pp.h" | ||||||
|  | #include "ast/ast_ll_pp.h" | ||||||
| #include "ast/ast_translation.h" | #include "ast/ast_translation.h" | ||||||
| 
 | 
 | ||||||
| namespace euf { | namespace euf { | ||||||
| 
 | 
 | ||||||
|     /**
 |  | ||||||
|        \brief Trail for add_th_var |  | ||||||
|     */ |  | ||||||
|     class add_th_var_trail : public trail<egraph> { |  | ||||||
|         enode *    m_enode; |  | ||||||
|         theory_id  m_th_id; |  | ||||||
|     public: |  | ||||||
|         add_th_var_trail(enode * n, theory_id th_id): |  | ||||||
|             m_enode(n), |  | ||||||
|             m_th_id(th_id) { |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         void undo(egraph & ctx) override { |  | ||||||
|             theory_var v = m_enode->get_th_var(m_th_id); |  | ||||||
|             SASSERT(v != null_theory_var); |  | ||||||
|             m_enode->del_th_var(m_th_id); |  | ||||||
|             enode * root = m_enode->get_root(); |  | ||||||
|             if (root != m_enode && root->get_th_var(m_th_id) == v)  |  | ||||||
|                 root->del_th_var(m_th_id); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|        \brief Trail for replace_th_var |  | ||||||
|     */ |  | ||||||
|     class replace_th_var_trail : public trail<egraph> { |  | ||||||
|         enode *    m_enode; |  | ||||||
|         unsigned   m_th_id:8; |  | ||||||
|         unsigned   m_old_th_var:24; |  | ||||||
|     public: |  | ||||||
|         replace_th_var_trail(enode * n, theory_id th_id, theory_var old_var): |  | ||||||
|             m_enode(n), |  | ||||||
|             m_th_id(th_id), |  | ||||||
|             m_old_th_var(old_var) { |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         void undo(egraph & ctx) override { |  | ||||||
|             SASSERT(m_enode->get_th_var(m_th_id) != null_theory_var); |  | ||||||
|             m_enode->replace_th_var(m_old_th_var, m_th_id); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) { |     void egraph::undo_eq(enode* r1, enode* n1, unsigned r2_num_parents) { | ||||||
|         enode* r2 = r1->get_root(); |         enode* r2 = r1->get_root(); | ||||||
|         r2->dec_class_size(r1->class_size()); |         r2->dec_class_size(r1->class_size()); | ||||||
|  | @ -82,6 +41,9 @@ namespace euf { | ||||||
|         enode* n = enode::mk(m_region, f, num_args, args); |         enode* n = enode::mk(m_region, f, num_args, args); | ||||||
|         m_nodes.push_back(n); |         m_nodes.push_back(n); | ||||||
|         m_exprs.push_back(f); |         m_exprs.push_back(f); | ||||||
|  |         push_node(n); | ||||||
|  |         for (unsigned i = 0; i < num_args; ++i) | ||||||
|  |             set_merge_enabled(args[i], true); | ||||||
|         return n; |         return n; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -104,8 +66,7 @@ namespace euf { | ||||||
|     void egraph::reinsert_equality(enode* p) { |     void egraph::reinsert_equality(enode* p) { | ||||||
|         SASSERT(is_equality(p)); |         SASSERT(is_equality(p)); | ||||||
|         if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) { |         if (p->get_arg(0)->get_root() == p->get_arg(1)->get_root()) { | ||||||
|             m_new_lits.push_back(enode_bool_pair(p, true)); |             add_literal(p, true); | ||||||
|             ++m_stats.m_num_eqs; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -114,19 +75,14 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void egraph::force_push() { |     void egraph::force_push() { | ||||||
|  |         if (m_num_scopes == 0) | ||||||
|  |             return; | ||||||
|         for (; m_num_scopes > 0; --m_num_scopes) { |         for (; m_num_scopes > 0; --m_num_scopes) { | ||||||
|             scope s; |             m_scopes.push_back(m_updates.size()); | ||||||
|             s.m_inconsistent = m_inconsistent; |  | ||||||
|             s.m_num_eqs = m_eqs.size(); |  | ||||||
|             s.m_num_nodes = m_nodes.size(); |  | ||||||
|             s.m_trail_sz  = m_trail.size(); |  | ||||||
|             s.m_new_lits_sz   = m_new_lits.size(); |  | ||||||
|             s.m_new_th_eqs_sz = m_new_th_eqs.size(); |  | ||||||
|             s.m_new_lits_qhead = m_new_lits_qhead; |  | ||||||
|             s.m_new_th_eqs_qhead = m_new_th_eqs_qhead; |  | ||||||
|             m_scopes.push_back(s); |  | ||||||
|             m_region.push_scope(); |             m_region.push_scope(); | ||||||
|         } |         } | ||||||
|  |         m_updates.push_back(update_record(m_new_lits_qhead, update_record::new_lits_qhead())); | ||||||
|  |         m_updates.push_back(update_record(m_new_th_eqs_qhead, update_record::new_th_eq_qhead())); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void egraph::update_children(enode* n) { |     void egraph::update_children(enode* n) { | ||||||
|  | @ -147,6 +103,7 @@ namespace euf { | ||||||
|             return n; |             return n; | ||||||
|         if (is_equality(n)) { |         if (is_equality(n)) { | ||||||
|             update_children(n); |             update_children(n); | ||||||
|  |             reinsert_equality(n); | ||||||
|             return n; |             return n; | ||||||
|         } |         } | ||||||
|         enode_bool_pair p = m_table.insert(n); |         enode_bool_pair p = m_table.insert(n); | ||||||
|  | @ -170,6 +127,18 @@ namespace euf { | ||||||
|             n->m_parents.finalize(); |             n->m_parents.finalize(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void egraph::add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r) { | ||||||
|  |         m_new_th_eqs.push_back(th_eq(id, v1, v2, c, r)); | ||||||
|  |         m_updates.push_back(update_record(update_record::new_th_eq())); | ||||||
|  |         ++m_stats.m_num_th_eqs; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void egraph::add_literal(enode* n, bool is_eq) { | ||||||
|  |         m_new_lits.push_back(enode_bool_pair(n, is_eq)); | ||||||
|  |         m_updates.push_back(update_record(update_record::new_lit())); | ||||||
|  |         if (is_eq) ++m_stats.m_num_eqs; else ++m_stats.m_num_lits; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void egraph::add_th_var(enode* n, theory_var v, theory_id id) { |     void egraph::add_th_var(enode* n, theory_var v, theory_id id) { | ||||||
|         force_push(); |         force_push(); | ||||||
|         theory_var w = n->get_th_var(id); |         theory_var w = n->get_th_var(id); | ||||||
|  | @ -177,21 +146,37 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|         if (w == null_theory_var) { |         if (w == null_theory_var) { | ||||||
|             n->add_th_var(v, id, m_region); |             n->add_th_var(v, id, m_region); | ||||||
|             m_trail.push_back(new (m_region) add_th_var_trail(n, id)); |             m_updates.push_back(update_record(n, id, update_record::add_th_var())); | ||||||
|             if (r != n) { |             if (r != n) { | ||||||
|                 theory_var u = r->get_th_var(id); |                 theory_var u = r->get_th_var(id); | ||||||
|                 if (u == null_theory_var)  |                 if (u == null_theory_var)  | ||||||
|                     r->add_th_var(v, id, m_region); |                     r->add_th_var(v, id, m_region); | ||||||
|                 else |                 else | ||||||
|                     m_new_th_eqs.push_back(th_eq(id, v, u, n, r)); |                     add_th_eq(id, v, u, n, r); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             theory_var u = r->get_th_var(id); |             theory_var u = r->get_th_var(id); | ||||||
|             SASSERT(u != v && u != null_theory_var); |             SASSERT(u != v && u != null_theory_var); | ||||||
|             n->replace_th_var(v, id); |             n->replace_th_var(v, id); | ||||||
|             m_trail.push_back(new (m_region) replace_th_var_trail(n, id, u)); |             m_updates.push_back(update_record(n, id, u, update_record::replace_th_var())); | ||||||
|             m_new_th_eqs.push_back(th_eq(id, v, u, n, r)); |             add_th_eq(id, v, u, n, r); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void egraph::undo_add_th_var(enode* n, theory_id tid) { | ||||||
|  |         theory_var v = n->get_th_var(tid); | ||||||
|  |         SASSERT(v != null_theory_var); | ||||||
|  |         n->del_th_var(tid); | ||||||
|  |         enode* root = n->get_root(); | ||||||
|  |         if (root != n && root->get_th_var(tid) == v) | ||||||
|  |             root->del_th_var(tid); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void egraph::set_merge_enabled(enode* n, bool enable_merge) { | ||||||
|  |         if (enable_merge != n->merge_enabled()) { | ||||||
|  |             m_updates.push_back(update_record(n, update_record::toggle_merge())); | ||||||
|  |             n->set_merge_enabled(enable_merge); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -202,29 +187,59 @@ namespace euf { | ||||||
|         } |         } | ||||||
|         num_scopes -= m_num_scopes; |         num_scopes -= m_num_scopes; | ||||||
|         unsigned old_lim = m_scopes.size() - num_scopes; |         unsigned old_lim = m_scopes.size() - num_scopes; | ||||||
|         scope s = m_scopes[old_lim]; |         unsigned num_updates = m_scopes[old_lim]; | ||||||
|         for (unsigned i = m_eqs.size(); i-- > s.m_num_eqs; ) { |         auto undo_node = [&](enode* n) { | ||||||
|             auto const& p = m_eqs[i]; |  | ||||||
|             undo_eq(p.r1, p.n1, p.r2_num_parents); |  | ||||||
|         }         |  | ||||||
|         for (unsigned i = m_nodes.size(); i-- > s.m_num_nodes; ) { |  | ||||||
|             enode* n = m_nodes[i]; |  | ||||||
|             if (n->num_args() > 1) |             if (n->num_args() > 1) | ||||||
|                 m_table.erase(n); |                 m_table.erase(n); | ||||||
|             m_expr2enode[n->get_owner_id()] = nullptr; |             m_expr2enode[n->get_owner_id()] = nullptr; | ||||||
|             n->~enode(); |             n->~enode(); | ||||||
|  |             m_nodes.pop_back(); | ||||||
|  |             m_exprs.pop_back(); | ||||||
|  |         }; | ||||||
|  |         for (unsigned i = m_updates.size(); i-- > num_updates; ) { | ||||||
|  |             auto const& p = m_updates[i]; | ||||||
|  |             switch (p.tag) { | ||||||
|  |             case update_record::tag_t::is_add_node: | ||||||
|  |                 undo_node(p.r1); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_toggle_merge: | ||||||
|  |                 p.r1->set_merge_enabled(!p.r1->merge_enabled()); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_set_parent: | ||||||
|  |                 undo_eq(p.r1, p.n1, p.r2_num_parents); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_add_th_var: | ||||||
|  |                 undo_add_th_var(p.r1, p.r2_num_parents); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_replace_th_var: | ||||||
|  |                 SASSERT(p.r1->get_th_var(p.m_th_id) != null_theory_var); | ||||||
|  |                 p.r1->replace_th_var(p.m_old_th_var, p.m_th_id); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_new_lit: | ||||||
|  |                 m_new_lits.pop_back(); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_new_th_eq: | ||||||
|  |                 m_new_th_eqs.pop_back(); | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_new_th_eq_qhead: | ||||||
|  |                 m_new_th_eqs_qhead = p.qhead; | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_new_lits_qhead: | ||||||
|  |                 m_new_lits_qhead = p.qhead; | ||||||
|  |                 break; | ||||||
|  |             case update_record::tag_t::is_inconsistent: | ||||||
|  |                 m_inconsistent = p.m_inconsistent; | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 UNREACHABLE(); | ||||||
|  |                 break; | ||||||
|             }                 |             }                 | ||||||
|         undo_trail_stack<egraph>(*this, m_trail, s.m_trail_sz); |         }         | ||||||
|         m_inconsistent = s.m_inconsistent; |         | ||||||
|         m_new_lits_qhead = s.m_new_lits_qhead; |         m_updates.shrink(num_updates); | ||||||
|         m_new_th_eqs_qhead = s.m_new_th_eqs_qhead; |  | ||||||
|         m_eqs.shrink(s.m_num_eqs); |  | ||||||
|         m_nodes.shrink(s.m_num_nodes); |  | ||||||
|         m_exprs.shrink(s.m_num_nodes); |  | ||||||
|         m_new_lits.shrink(s.m_new_lits_sz); |  | ||||||
|         m_new_th_eqs.shrink(s.m_new_th_eqs_sz); |  | ||||||
|         m_scopes.shrink(old_lim);         |         m_scopes.shrink(old_lim);         | ||||||
|         m_region.pop_scope(num_scopes);   |         m_region.pop_scope(num_scopes);   | ||||||
|  |         m_worklist.reset(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void egraph::merge(enode* n1, enode* n2, justification j) { |     void egraph::merge(enode* n1, enode* n2, justification j) { | ||||||
|  | @ -245,8 +260,7 @@ namespace euf { | ||||||
|             std::swap(n1, n2); |             std::swap(n1, n2); | ||||||
|         } |         } | ||||||
|         if ((m.is_true(r2->get_owner()) || m.is_false(r2->get_owner())) && j.is_congruence()) { |         if ((m.is_true(r2->get_owner()) || m.is_false(r2->get_owner())) && j.is_congruence()) { | ||||||
|             m_new_lits.push_back(enode_bool_pair(n1, false)); |             add_literal(n1, false); | ||||||
|             ++m_stats.m_num_lits; |  | ||||||
|         } |         } | ||||||
|         for (enode* p : enode_parents(n1))  |         for (enode* p : enode_parents(n1))  | ||||||
|             m_table.erase(p);             |             m_table.erase(p);             | ||||||
|  | @ -270,11 +284,11 @@ namespace euf { | ||||||
|             theory_var v = root->get_th_var(id); |             theory_var v = root->get_th_var(id); | ||||||
|             if (v == null_theory_var) {                 |             if (v == null_theory_var) {                 | ||||||
|                 root->add_th_var(iv.get_var(), id, m_region);    |                 root->add_th_var(iv.get_var(), id, m_region);    | ||||||
|                 m_trail.push_back(new (m_region) add_th_var_trail(root, id)); |                 m_updates.push_back(update_record(root, id, update_record::add_th_var())); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 SASSERT(v != iv.get_var()); |                 SASSERT(v != iv.get_var()); | ||||||
|                 m_new_th_eqs.push_back(th_eq(id, v, iv.get_var(), n, root)); |                 add_th_eq(id, v, iv.get_var(), n, root); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -308,6 +322,7 @@ namespace euf { | ||||||
|         if (m_inconsistent) |         if (m_inconsistent) | ||||||
|             return; |             return; | ||||||
|         m_inconsistent = true; |         m_inconsistent = true; | ||||||
|  |         m_updates.push_back(update_record(false, update_record::inconsistent())); | ||||||
|         m_n1 = n1; |         m_n1 = n1; | ||||||
|         m_n2 = n2; |         m_n2 = n2; | ||||||
|         m_justification = j; |         m_justification = j; | ||||||
|  | @ -387,10 +402,19 @@ namespace euf { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void egraph::begin_explain() { | ||||||
|  |         SASSERT(m_todo.empty()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void egraph::end_explain() { | ||||||
|  |         for (enode* n : m_todo)  | ||||||
|  |             n->unmark1(); | ||||||
|  |         m_todo.reset();         | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     void egraph::explain(ptr_vector<T>& justifications) { |     void egraph::explain(ptr_vector<T>& justifications) { | ||||||
|         SASSERT(m_inconsistent); |         SASSERT(m_inconsistent); | ||||||
|         SASSERT(m_todo.empty()); |  | ||||||
|         push_todo(m_n1); |         push_todo(m_n1); | ||||||
|         push_todo(m_n2); |         push_todo(m_n2); | ||||||
|         explain_eq(justifications, m_n1, m_n2, m_justification); |         explain_eq(justifications, m_n1, m_n2, m_justification); | ||||||
|  | @ -399,7 +423,6 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|     template <typename T> |     template <typename T> | ||||||
|     void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) { |     void egraph::explain_eq(ptr_vector<T>& justifications, enode* a, enode* b) { | ||||||
|         SASSERT(m_todo.empty()); |  | ||||||
|         SASSERT(a->get_root() == b->get_root()); |         SASSERT(a->get_root() == b->get_root()); | ||||||
|         enode* lca = find_lca(a, b); |         enode* lca = find_lca(a, b); | ||||||
|         push_to_lca(a, lca); |         push_to_lca(a, lca); | ||||||
|  | @ -418,9 +441,6 @@ namespace euf { | ||||||
|                 explain_eq(justifications, n, n->m_target, n->m_justification); |                 explain_eq(justifications, n, n->m_target, n->m_justification); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         for (enode* n : m_todo)  |  | ||||||
|             n->unmark1(); |  | ||||||
|         m_todo.reset();         |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void egraph::invariant() { |     void egraph::invariant() { | ||||||
|  | @ -429,25 +449,29 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const { |     std::ostream& egraph::display(std::ostream& out, unsigned max_args, enode* n) const { | ||||||
|         out << std::setw(5) |         out << n->get_owner_id() << " := "; | ||||||
|             << n->get_owner_id() << " := "; |  | ||||||
|         if (!n->is_root())  |         if (!n->is_root())  | ||||||
|             out << "[" << n->get_root()->get_owner_id() << "] "; |             out << "[" << n->get_root()->get_owner_id() << "] "; | ||||||
|         expr* f = n->get_owner(); |         expr* f = n->get_owner(); | ||||||
|         if (is_app(f)) |         if (is_app(f)) | ||||||
|             out << to_app(f)->get_decl()->get_name() << " "; |             out << mk_bounded_pp(f, m, 1); | ||||||
|         else if (is_quantifier(f)) |         else if (is_quantifier(f)) | ||||||
|             out << "q "; |             out << "q:" << f->get_id(); | ||||||
|         else |         else | ||||||
|             out << "v "; |             out << "v:" << f->get_id(); | ||||||
|         for (enode* arg : enode_args(n)) |         out << "\n"; | ||||||
|             out << arg->get_owner_id() << " "; |         if (!n->m_parents.empty()) { | ||||||
|         for (unsigned i = n->num_args(); i < max_args; ++i) |  | ||||||
|             out << "    "; |             out << "    "; | ||||||
|         out << "\t"; |  | ||||||
|             for (enode* p : enode_parents(n)) |             for (enode* p : enode_parents(n)) | ||||||
|                 out << p->get_owner_id() << " "; |                 out << p->get_owner_id() << " "; | ||||||
|             out << "\n"; |             out << "\n"; | ||||||
|  |         } | ||||||
|  |         if (n->has_th_vars()) { | ||||||
|  |             out << "    "; | ||||||
|  |             for (auto v : enode_th_vars(n)) | ||||||
|  |                 out << v.get_id() << ":" << v.get_var() << " "; | ||||||
|  |             out << "\n"; | ||||||
|  |         } | ||||||
|         return out; |         return out; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -464,8 +488,9 @@ namespace euf { | ||||||
|     void egraph::collect_statistics(statistics& st) const { |     void egraph::collect_statistics(statistics& st) const { | ||||||
|         st.update("euf merge", m_stats.m_num_merge); |         st.update("euf merge", m_stats.m_num_merge); | ||||||
|         st.update("euf conflicts", m_stats.m_num_conflicts); |         st.update("euf conflicts", m_stats.m_num_conflicts); | ||||||
|         st.update("euf eq prop", m_stats.m_num_eqs); |         st.update("euf equality propagations", m_stats.m_num_eqs); | ||||||
|         st.update("euf lit prop", m_stats.m_num_lits); |         st.update("euf theory equality propagations", m_stats.m_num_th_eqs); | ||||||
|  |         st.update("euf literal propagations", m_stats.m_num_lits); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) { |     void egraph::copy_from(egraph const& src, std::function<void*(void*)>& copy_justification) { | ||||||
|  | @ -504,7 +529,7 @@ template void euf::egraph::explain(ptr_vector<int>& justifications); | ||||||
| template void euf::egraph::explain_todo(ptr_vector<int>& justifications); | template void euf::egraph::explain_todo(ptr_vector<int>& justifications); | ||||||
| template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b); | template void euf::egraph::explain_eq(ptr_vector<int>& justifications, enode* a, enode* b); | ||||||
| 
 | 
 | ||||||
| template void euf::egraph::explain(ptr_vector<unsigned>& justifications); | template void euf::egraph::explain(ptr_vector<size_t>& justifications); | ||||||
| template void euf::egraph::explain_todo(ptr_vector<unsigned>& justifications); | template void euf::egraph::explain_todo(ptr_vector<size_t>& justifications); | ||||||
| template void euf::egraph::explain_eq(ptr_vector<unsigned>& justifications, enode* a, enode* b); | template void euf::egraph::explain_eq(ptr_vector<size_t>& justifications, enode* a, enode* b); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -31,14 +31,6 @@ Notes: | ||||||
| 
 | 
 | ||||||
| namespace euf { | namespace euf { | ||||||
| 
 | 
 | ||||||
|     struct add_eq_record { |  | ||||||
|         enode* r1; |  | ||||||
|         enode* n1; |  | ||||||
|         unsigned r2_num_parents; |  | ||||||
|         add_eq_record(enode* r1, enode* n1, unsigned r2_num_parents): |  | ||||||
|             r1(r1), n1(n1), r2_num_parents(r2_num_parents) {} |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /***
 |     /***
 | ||||||
|       \brief store derived theory equalities. |       \brief store derived theory equalities. | ||||||
|       Theory 'id' is notified with the equality of theory variables v1, v2 |       Theory 'id' is notified with the equality of theory variables v1, v2 | ||||||
|  | @ -58,31 +50,66 @@ namespace euf { | ||||||
|      |      | ||||||
|     class egraph {         |     class egraph {         | ||||||
|         typedef ptr_vector<trail<egraph> > trail_stack; |         typedef ptr_vector<trail<egraph> > trail_stack; | ||||||
|         struct scope { |  | ||||||
|             bool     m_inconsistent; |  | ||||||
|             unsigned m_num_eqs; |  | ||||||
|             unsigned m_num_nodes; |  | ||||||
|             unsigned m_trail_sz; |  | ||||||
|             unsigned m_new_lits_sz; |  | ||||||
|             unsigned m_new_th_eqs_sz; |  | ||||||
|             unsigned m_new_lits_qhead; |  | ||||||
|             unsigned m_new_th_eqs_qhead; |  | ||||||
|         }; |  | ||||||
|         struct stats { |         struct stats { | ||||||
|             unsigned m_num_merge; |             unsigned m_num_merge; | ||||||
|  |             unsigned m_num_th_eqs; | ||||||
|             unsigned m_num_lits; |             unsigned m_num_lits; | ||||||
|             unsigned m_num_eqs; |             unsigned m_num_eqs; | ||||||
|             unsigned m_num_conflicts; |             unsigned m_num_conflicts; | ||||||
|             stats() { reset(); } |             stats() { reset(); } | ||||||
|             void reset() { memset(this, 0, sizeof(*this)); } |             void reset() { memset(this, 0, sizeof(*this)); } | ||||||
|         }; |         }; | ||||||
|  |         struct update_record { | ||||||
|  |             struct toggle_merge {}; | ||||||
|  |             struct add_th_var {}; | ||||||
|  |             struct replace_th_var {}; | ||||||
|  |             struct new_lit {}; | ||||||
|  |             struct new_th_eq {}; | ||||||
|  |             struct new_th_eq_qhead {}; | ||||||
|  |             struct new_lits_qhead {}; | ||||||
|  |             struct inconsistent {}; | ||||||
|  |             enum tag_t { is_set_parent, is_add_node, is_toggle_merge,  | ||||||
|  |                          is_add_th_var, is_replace_th_var, is_new_lit, is_new_th_eq, | ||||||
|  |                          is_new_th_eq_qhead, is_new_lits_qhead, is_inconsistent }; | ||||||
|  |             tag_t  tag; | ||||||
|  |             enode* r1; | ||||||
|  |             enode* n1; | ||||||
|  |             union { | ||||||
|  |                 unsigned r2_num_parents; | ||||||
|  |                 struct { | ||||||
|  |                     unsigned   m_th_id : 8; | ||||||
|  |                     unsigned   m_old_th_var : 24; | ||||||
|  |                 }; | ||||||
|  |                 unsigned qhead; | ||||||
|  |                 bool     m_inconsistent; | ||||||
|  |             }; | ||||||
|  |             update_record(enode* r1, enode* n1, unsigned r2_num_parents) : | ||||||
|  |                 tag(tag_t::is_set_parent), r1(r1), n1(n1), r2_num_parents(r2_num_parents) {} | ||||||
|  |             update_record(enode* n) : | ||||||
|  |                 tag(tag_t::is_add_node), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} | ||||||
|  |             update_record(enode* n, toggle_merge) : | ||||||
|  |                 tag(tag_t::is_toggle_merge), r1(n), n1(nullptr), r2_num_parents(UINT_MAX) {} | ||||||
|  |             update_record(enode* n, unsigned id, add_th_var) : | ||||||
|  |                 tag(tag_t::is_add_th_var), r1(n), n1(nullptr), r2_num_parents(id) {} | ||||||
|  |             update_record(enode* n, theory_id id, theory_var v, replace_th_var) : | ||||||
|  |                 tag(tag_t::is_replace_th_var), r1(n), n1(nullptr), m_th_id(id), m_old_th_var(v) {} | ||||||
|  |             update_record(new_lit) : | ||||||
|  |                 tag(tag_t::is_new_lit), r1(nullptr), n1(nullptr), r2_num_parents(0) {} | ||||||
|  |             update_record(new_th_eq) : | ||||||
|  |                 tag(tag_t::is_new_th_eq), r1(nullptr), n1(nullptr), r2_num_parents(0) {} | ||||||
|  |             update_record(unsigned qh, new_th_eq_qhead): | ||||||
|  |                 tag(tag_t::is_new_th_eq_qhead), r1(nullptr), n1(nullptr), qhead(qh) {} | ||||||
|  |             update_record(unsigned qh, new_lits_qhead): | ||||||
|  |                 tag(tag_t::is_new_lits_qhead), r1(nullptr), n1(nullptr), qhead(qh) {} | ||||||
|  |             update_record(bool inc, inconsistent) : | ||||||
|  |                 tag(tag_t::is_inconsistent), m_inconsistent(inc) {} | ||||||
|  |         }; | ||||||
|         ast_manager&           m; |         ast_manager&           m; | ||||||
|         trail_stack            m_trail; |  | ||||||
|         region                 m_region; |  | ||||||
|         enode_vector           m_worklist; |         enode_vector           m_worklist; | ||||||
|         etable                 m_table; |         etable                 m_table; | ||||||
|         svector<add_eq_record> m_eqs; |         region                 m_region; | ||||||
|         svector<scope>         m_scopes; |         svector<update_record> m_updates; | ||||||
|  |         unsigned_vector        m_scopes; | ||||||
|         enode_vector           m_expr2enode; |         enode_vector           m_expr2enode; | ||||||
|         enode_vector           m_nodes; |         enode_vector           m_nodes; | ||||||
|         expr_ref_vector        m_exprs; |         expr_ref_vector        m_exprs; | ||||||
|  | @ -101,9 +128,14 @@ namespace euf { | ||||||
|         std::function<void(app*,app*)>        m_used_cc;   |         std::function<void(app*,app*)>        m_used_cc;   | ||||||
| 
 | 
 | ||||||
|         void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) { |         void push_eq(enode* r1, enode* n1, unsigned r2_num_parents) { | ||||||
|             m_eqs.push_back(add_eq_record(r1, n1, r2_num_parents)); |             m_updates.push_back(update_record(r1, n1, r2_num_parents)); | ||||||
|         } |         } | ||||||
|  |         void push_node(enode* n) { m_updates.push_back(update_record(n)); } | ||||||
|  | 
 | ||||||
|  |         void add_th_eq(theory_id id, theory_var v1, theory_var v2, enode* c, enode* r); | ||||||
|  |         void add_literal(enode* n, bool is_eq); | ||||||
|         void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); |         void undo_eq(enode* r1, enode* n1, unsigned r2_num_parents); | ||||||
|  |         void undo_add_th_var(enode* n, theory_id id); | ||||||
|         enode* mk_enode(expr* f, unsigned num_args, enode * const* args); |         enode* mk_enode(expr* f, unsigned num_args, enode * const* args); | ||||||
|         void reinsert(enode* n); |         void reinsert(enode* n); | ||||||
|         void force_push(); |         void force_push(); | ||||||
|  | @ -173,10 +205,13 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         void add_th_var(enode* n, theory_var v, theory_id id); |         void add_th_var(enode* n, theory_var v, theory_id id); | ||||||
|  |         void set_merge_enabled(enode* n, bool enable_merge); | ||||||
| 
 | 
 | ||||||
|         void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; } |         void set_used_eq(std::function<void(expr*,expr*,expr*)>& used_eq) { m_used_eq = used_eq; } | ||||||
|         void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; } |         void set_used_cc(std::function<void(app*,app*)>& used_cc) { m_used_cc = used_cc; } | ||||||
| 
 | 
 | ||||||
|  |         void begin_explain(); | ||||||
|  |         void end_explain(); | ||||||
|         template <typename T> |         template <typename T> | ||||||
|         void explain(ptr_vector<T>& justifications); |         void explain(ptr_vector<T>& justifications); | ||||||
|         template <typename T> |         template <typename T> | ||||||
|  |  | ||||||
|  | @ -36,21 +36,22 @@ namespace euf { | ||||||
|     const theory_id null_theory_id = -1; |     const theory_id null_theory_id = -1; | ||||||
| 
 | 
 | ||||||
|     class enode { |     class enode { | ||||||
|         expr*         m_owner; |         expr*         m_owner{ nullptr }; | ||||||
|         bool          m_mark1 { false }; |         bool          m_mark1 { false }; | ||||||
|         bool          m_mark2 { false }; |         bool          m_mark2 { false }; | ||||||
|         bool          m_commutative { false }; |         bool          m_commutative { false }; | ||||||
|         bool          m_update_children { false }; |         bool          m_update_children { false }; | ||||||
|         bool          m_interpreted { false }; |         bool          m_interpreted { false }; | ||||||
|  |         bool          m_merge_enabled { true }; | ||||||
|         unsigned      m_class_size { 1 }; |         unsigned      m_class_size { 1 }; | ||||||
|         unsigned      m_table_id { UINT_MAX }; |         unsigned      m_table_id { UINT_MAX }; | ||||||
|         enode_vector  m_parents; |         enode_vector  m_parents; | ||||||
|         enode*        m_next; |         enode*        m_next{ nullptr }; | ||||||
|         enode*        m_root; |         enode*        m_root{ nullptr }; | ||||||
|         enode*        m_target { nullptr }; |         enode*        m_target { nullptr }; | ||||||
|         th_var_list   m_th_vars; |         th_var_list   m_th_vars; | ||||||
|         justification m_justification; |         justification m_justification; | ||||||
|         unsigned      m_num_args; |         unsigned      m_num_args { 0 }; | ||||||
|         enode*        m_args[0];         |         enode*        m_args[0];         | ||||||
| 
 | 
 | ||||||
|         friend class enode_args; |         friend class enode_args; | ||||||
|  | @ -73,6 +74,7 @@ namespace euf { | ||||||
|             n->m_root = n; |             n->m_root = n; | ||||||
|             n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative(); |             n->m_commutative = num_args == 2 && is_app(f) && to_app(f)->get_decl()->is_commutative(); | ||||||
|             n->m_num_args = num_args; |             n->m_num_args = num_args; | ||||||
|  |             n->m_merge_enabled = true; | ||||||
|             for (unsigned i = 0; i < num_args; ++i) { |             for (unsigned i = 0; i < num_args; ++i) { | ||||||
|                 SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner()); |                 SASSERT(to_app(f)->get_arg(i) == args[i]->get_owner()); | ||||||
|                 n->m_args[i] = args[i]; |                 n->m_args[i] = args[i]; | ||||||
|  | @ -87,6 +89,7 @@ namespace euf { | ||||||
|         void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); } |         void add_th_var(theory_var v, theory_id id, region & r) { m_th_vars.add_var(v, id, r); } | ||||||
|         void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); } |         void replace_th_var(theory_var v, theory_id id) { m_th_vars.replace(v, id); } | ||||||
|         void del_th_var(theory_id id) { m_th_vars.del_var(id); }    |         void del_th_var(theory_id id) { m_th_vars.del_var(id); }    | ||||||
|  |         void set_merge_enabled(bool m) { m_merge_enabled = m; } | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|         ~enode() {  |         ~enode() {  | ||||||
|  | @ -106,6 +109,7 @@ namespace euf { | ||||||
|         bool interpreted() const { return m_interpreted; } |         bool interpreted() const { return m_interpreted; } | ||||||
|         bool commutative() const { return m_commutative; } |         bool commutative() const { return m_commutative; } | ||||||
|         void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; } |         void mark_interpreted() { SASSERT(num_args() == 0); m_interpreted = true; } | ||||||
|  |         bool merge_enabled() { return m_merge_enabled; } | ||||||
| 
 | 
 | ||||||
|         enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }         |         enode* get_arg(unsigned i) const { SASSERT(i < num_args()); return m_args[i]; }         | ||||||
|         unsigned hash() const { return m_owner->hash(); } |         unsigned hash() const { return m_owner->hash(); } | ||||||
|  | @ -143,6 +147,7 @@ namespace euf { | ||||||
|         unsigned get_owner_id() const { return m_owner->get_id(); } |         unsigned get_owner_id() const { return m_owner->get_id(); } | ||||||
|         unsigned get_root_id() const { return m_root->m_owner->get_id(); } |         unsigned get_root_id() const { return m_root->m_owner->get_id(); } | ||||||
|         theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); } |         theory_var get_th_var(theory_id id) const { return m_th_vars.find(id); } | ||||||
|  |         bool is_attached_to(theory_id id) const { return get_th_var(id) != null_theory_var; } | ||||||
|         bool has_th_vars() const { return !m_th_vars.empty(); } |         bool has_th_vars() const { return !m_th_vars.empty(); } | ||||||
| 
 | 
 | ||||||
|         void inc_class_size(unsigned n) { m_class_size += n; } |         void inc_class_size(unsigned n) { m_class_size += n; } | ||||||
|  |  | ||||||
|  | @ -45,14 +45,14 @@ class expr_pattern_match { | ||||||
|             m_kind(k), m_offset(o), m_next(next), m_app(app), m_count(count) {} |             m_kind(k), m_offset(o), m_next(next), m_app(app), m_count(count) {} | ||||||
| 
 | 
 | ||||||
|         instr_kind      m_kind; |         instr_kind      m_kind; | ||||||
|         unsigned        m_offset; |         unsigned        m_offset{ 0 }; | ||||||
|         unsigned        m_next; |         unsigned        m_next{ 0 }; | ||||||
|         app*            m_app; |         app*            m_app{ nullptr }; | ||||||
|         expr*           m_pat; |         expr*           m_pat{ nullptr }; | ||||||
|         unsigned        m_reg; |         unsigned        m_reg{ 0 }; | ||||||
|         unsigned        m_other_reg; |         unsigned        m_other_reg{ 0 }; | ||||||
|         unsigned        m_count; |         unsigned        m_count{ 0 }; | ||||||
|         unsigned        m_num_bound; |         unsigned        m_num_bound{ 0 }; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     typedef obj_map<func_decl, unsigned> subst; |     typedef obj_map<func_decl, unsigned> subst; | ||||||
|  |  | ||||||
|  | @ -30,7 +30,6 @@ Notes: | ||||||
| #include "ast/seq_decl_plugin.h" | #include "ast/seq_decl_plugin.h" | ||||||
| #include "ast/pb_decl_plugin.h" | #include "ast/pb_decl_plugin.h" | ||||||
| #include "ast/fpa_decl_plugin.h" | #include "ast/fpa_decl_plugin.h" | ||||||
| #include "ast/csp_decl_plugin.h" |  | ||||||
| #include "ast/special_relations_decl_plugin.h" | #include "ast/special_relations_decl_plugin.h" | ||||||
| #include "ast/ast_pp.h" | #include "ast/ast_pp.h" | ||||||
| #include "ast/rewriter/var_subst.h" | #include "ast/rewriter/var_subst.h" | ||||||
|  | @ -698,7 +697,6 @@ void cmd_context::init_manager_core(bool new_manager) { | ||||||
|         register_plugin(symbol("pb"),       alloc(pb_decl_plugin), logic_has_pb()); |         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("fpa"),      alloc(fpa_decl_plugin), logic_has_fpa()); | ||||||
|         register_plugin(symbol("datalog_relation"), alloc(datalog::dl_decl_plugin), !has_logic()); |         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)); |  | ||||||
|         register_plugin(symbol("specrels"), alloc(special_relations_decl_plugin), !has_logic()); |         register_plugin(symbol("specrels"), alloc(special_relations_decl_plugin), !has_logic()); | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|  | @ -715,7 +713,6 @@ void cmd_context::init_manager_core(bool new_manager) { | ||||||
|         load_plugin(symbol("seq"),      logic_has_seq(), fids); |         load_plugin(symbol("seq"),      logic_has_seq(), fids); | ||||||
|         load_plugin(symbol("fpa"),      logic_has_fpa(), fids); |         load_plugin(symbol("fpa"),      logic_has_fpa(), fids); | ||||||
|         load_plugin(symbol("pb"),       logic_has_pb(), 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) { |         for (family_id fid : fids) { | ||||||
|             decl_plugin * p = m_manager->get_plugin(fid); |             decl_plugin * p = m_manager->get_plugin(fid); | ||||||
|  |  | ||||||
|  | @ -20,43 +20,21 @@ Revision History: | ||||||
| #undef max | #undef max | ||||||
| #undef min | #undef min | ||||||
| #include "sat/sat_solver.h" | #include "sat/sat_solver.h" | ||||||
| 
 | #include "sat/sat_drat.h" | ||||||
| namespace { |  | ||||||
| struct lex_error {}; |  | ||||||
| 
 |  | ||||||
| class stream_buffer { |  | ||||||
|     std::istream & m_stream; |  | ||||||
|     int            m_val; |  | ||||||
|     unsigned       m_line; |  | ||||||
| public: |  | ||||||
|      |  | ||||||
|     stream_buffer(std::istream & s): |  | ||||||
|         m_stream(s), |  | ||||||
|         m_line(0) { |  | ||||||
|         m_val = m_stream.get(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int  operator *() const {  |  | ||||||
|         return m_val; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void operator ++() {  |  | ||||||
|         m_val = m_stream.get(); |  | ||||||
|         if (m_val == '\n') ++m_line; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     unsigned line() const { return m_line; } |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| template<typename Buffer> | template<typename Buffer> | ||||||
| void skip_whitespace(Buffer & in) { | static bool is_whitespace(Buffer & in) { | ||||||
|     while ((*in >= 9 && *in <= 13) || *in == 32) { |     return (*in >= 9 && *in <= 13) || *in == 32; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename Buffer> | ||||||
|  | static void skip_whitespace(Buffer & in) { | ||||||
|  |     while (is_whitespace(in)) | ||||||
|         ++in;  |         ++in;  | ||||||
| } | } | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| template<typename Buffer> | template<typename Buffer> | ||||||
| void skip_line(Buffer & in) { | static void skip_line(Buffer & in) { | ||||||
|     while(true) { |     while(true) { | ||||||
|         if (*in == EOF) { |         if (*in == EOF) { | ||||||
|             return; |             return; | ||||||
|  | @ -70,7 +48,7 @@ void skip_line(Buffer & in) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<typename Buffer> | template<typename Buffer> | ||||||
| int parse_int(Buffer & in, std::ostream& err) { | static int parse_int(Buffer & in, std::ostream& err) { | ||||||
|     int     val = 0; |     int     val = 0; | ||||||
|     bool    neg = false; |     bool    neg = false; | ||||||
|     skip_whitespace(in); |     skip_whitespace(in); | ||||||
|  | @ -84,8 +62,11 @@ int parse_int(Buffer & in, std::ostream& err) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (*in < '0' || *in > '9') { |     if (*in < '0' || *in > '9') { | ||||||
|  |         if (20 <= *in && *in < 128)  | ||||||
|  |             err << "(error, \"unexpected char: " << ((char)*in) << " line: " << in.line() << "\")\n"; | ||||||
|  |         else | ||||||
|             err << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n"; |             err << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n"; | ||||||
|         throw lex_error(); |         throw dimacs::lex_error(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     while (*in >= '0' && *in <= '9') { |     while (*in >= '0' && *in <= '9') { | ||||||
|  | @ -97,7 +78,7 @@ int parse_int(Buffer & in, std::ostream& err) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<typename Buffer> | template<typename Buffer> | ||||||
| void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::literal_vector & lits) { | static void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::literal_vector & lits) { | ||||||
|     int     parsed_lit; |     int     parsed_lit; | ||||||
|     int     var; |     int     var; | ||||||
|      |      | ||||||
|  | @ -116,7 +97,24 @@ void read_clause(Buffer & in, std::ostream& err, sat::solver & solver, sat::lite | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<typename Buffer> | template<typename Buffer> | ||||||
| bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) { | static void read_clause(Buffer & in, std::ostream& err, sat::literal_vector & lits) { | ||||||
|  |     int     parsed_lit; | ||||||
|  |     int     var; | ||||||
|  |      | ||||||
|  |     lits.reset(); | ||||||
|  | 
 | ||||||
|  |     while (true) {  | ||||||
|  |         parsed_lit = parse_int(in, err); | ||||||
|  |         if (parsed_lit == 0) | ||||||
|  |             break; | ||||||
|  |         var = abs(parsed_lit); | ||||||
|  |         SASSERT(var > 0); | ||||||
|  |         lits.push_back(sat::literal(var, parsed_lit < 0)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename Buffer> | ||||||
|  | static bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) { | ||||||
|     sat::literal_vector lits; |     sat::literal_vector lits; | ||||||
|     try { |     try { | ||||||
|         while (true) { |         while (true) { | ||||||
|  | @ -133,15 +131,158 @@ bool parse_dimacs_core(Buffer & in, std::ostream& err, sat::solver & solver) { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     catch (lex_error) { |     catch (dimacs::lex_error) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| bool parse_dimacs(std::istream & in, std::ostream& err, sat::solver & solver) { | bool parse_dimacs(std::istream & in, std::ostream& err, sat::solver & solver) { | ||||||
|     stream_buffer _in(in); |     dimacs::stream_buffer _in(in); | ||||||
|     return parse_dimacs_core(_in, err, solver); |     return parse_dimacs_core(_in, err, solver); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace dimacs { | ||||||
|  | 
 | ||||||
|  |     std::ostream& operator<<(std::ostream& out, drat_record const& r) { | ||||||
|  |         switch (r.m_tag) { | ||||||
|  |         case drat_record::tag_t::is_clause: | ||||||
|  |             return out << r.m_status << " " << r.m_lits << "\n"; | ||||||
|  |         case drat_record::tag_t::is_node: | ||||||
|  |             return out << "e " << r.m_node_id << " " << r.m_name << " " << r.m_args << "\n";  | ||||||
|  |         case drat_record::is_bool_def: | ||||||
|  |             return out << "b " << r.m_node_id << " " << r.m_args << "\n"; | ||||||
|  |         } | ||||||
|  |         return out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     char const* drat_parser::parse_identifier() { | ||||||
|  |         m_buffer.reset(); | ||||||
|  |         while (!is_whitespace(in)) { | ||||||
|  |             m_buffer.push_back(*in); | ||||||
|  |             ++in; | ||||||
|  |         } | ||||||
|  |         m_buffer.push_back(0); | ||||||
|  |         return m_buffer.c_ptr(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     char const* drat_parser::parse_sexpr() { | ||||||
|  |         m_buffer.reset(); | ||||||
|  |         unsigned lp = 0; | ||||||
|  |         while (!is_whitespace(in) || lp > 0) { | ||||||
|  |             m_buffer.push_back(*in); | ||||||
|  |             if (*in == '(')  | ||||||
|  |                 ++lp; | ||||||
|  |             else if (*in == ')') { | ||||||
|  |                 if (lp == 0) {  | ||||||
|  |                     throw lex_error();  | ||||||
|  |                 }  | ||||||
|  |                 else --lp; | ||||||
|  |             } | ||||||
|  |             ++in; | ||||||
|  |         } | ||||||
|  |         m_buffer.push_back(0); | ||||||
|  |         return m_buffer.c_ptr();         | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int drat_parser::read_theory_id() { | ||||||
|  |         skip_whitespace(in); | ||||||
|  |         if ('a' <= *in && *in <= 'z') { | ||||||
|  |             if (!m_read_theory_id) | ||||||
|  |                 throw lex_error(); | ||||||
|  |             return m_read_theory_id(parse_identifier()); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool drat_parser::next() { | ||||||
|  |         int n, b, e, theory_id; | ||||||
|  |         try { | ||||||
|  |         loop: | ||||||
|  |             skip_whitespace(in); | ||||||
|  |             switch (*in) { | ||||||
|  |             case EOF: | ||||||
|  |                 return false; | ||||||
|  |             case 'c': | ||||||
|  |             case 'p': | ||||||
|  |                 skip_line(in); | ||||||
|  |                 goto loop; | ||||||
|  |             case 'i': | ||||||
|  |                 ++in; | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 read_clause(in, err, m_record.m_lits); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_clause; | ||||||
|  |                 m_record.m_status = sat::status::input(); | ||||||
|  |                 break; | ||||||
|  |             case 'a': | ||||||
|  |                 ++in; | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 theory_id = read_theory_id(); | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 read_clause(in, err, m_record.m_lits); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_clause; | ||||||
|  |                 m_record.m_status = sat::status::th(false, theory_id); | ||||||
|  |                 break; | ||||||
|  |             case 'e': | ||||||
|  |                 ++in; | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 n = parse_int(in, err);                     | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 m_record.m_name = parse_sexpr(); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_node; | ||||||
|  |                 m_record.m_node_id = n; | ||||||
|  |                 m_record.m_args.reset(); | ||||||
|  |                 while (true) { | ||||||
|  |                     n = parse_int(in, err); | ||||||
|  |                     if (n == 0) | ||||||
|  |                         break; | ||||||
|  |                     if (n < 0) | ||||||
|  |                         throw lex_error(); | ||||||
|  |                     m_record.m_args.push_back(n); | ||||||
|  |                 } | ||||||
|  |                 break; | ||||||
|  |             case 'b': | ||||||
|  |                 ++in; | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 b = parse_int(in, err); | ||||||
|  |                 n = parse_int(in, err); | ||||||
|  |                 e = parse_int(in, err); | ||||||
|  |                 if (e != 0) | ||||||
|  |                     throw lex_error(); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_bool_def; | ||||||
|  |                 m_record.m_node_id = b; | ||||||
|  |                 m_record.m_args.reset(); | ||||||
|  |                 m_record.m_args.push_back(n); | ||||||
|  |                 break; | ||||||
|  |             case 'd': | ||||||
|  |                 ++in; | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 read_clause(in, err, m_record.m_lits); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_clause; | ||||||
|  |                 m_record.m_status = sat::status::deleted(); | ||||||
|  |                 break; | ||||||
|  |             case 'r': | ||||||
|  |                 ++in; | ||||||
|  |                 skip_whitespace(in); | ||||||
|  |                 theory_id = read_theory_id(); | ||||||
|  |                 read_clause(in, err, m_record.m_lits); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_clause; | ||||||
|  |                 m_record.m_status = sat::status::th(true, theory_id); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 read_clause(in, err, m_record.m_lits); | ||||||
|  |                 m_record.m_tag = drat_record::tag_t::is_clause; | ||||||
|  |                 m_record.m_status = sat::status::redundant(); | ||||||
|  |                 break;                 | ||||||
|  |             }     | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         catch (lex_error) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -15,6 +15,9 @@ Author: | ||||||
| 
 | 
 | ||||||
| Revision History: | Revision History: | ||||||
| 
 | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2020-09-07 | ||||||
|  |     Add parser to consume extended DRAT format. | ||||||
|  | 
 | ||||||
| --*/ | --*/ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | @ -22,4 +25,87 @@ Revision History: | ||||||
| 
 | 
 | ||||||
| bool parse_dimacs(std::istream & s, std::ostream& err, sat::solver & solver); | bool parse_dimacs(std::istream & s, std::ostream& err, sat::solver & solver); | ||||||
| 
 | 
 | ||||||
|  | namespace dimacs { | ||||||
|  |     struct lex_error {}; | ||||||
|  | 
 | ||||||
|  |     class stream_buffer { | ||||||
|  |         std::istream & m_stream; | ||||||
|  |         int            m_val; | ||||||
|  |         unsigned       m_line; | ||||||
|  |     public: | ||||||
|  |          | ||||||
|  |     stream_buffer(std::istream & s): | ||||||
|  |         m_stream(s), | ||||||
|  |             m_line(0) { | ||||||
|  |             m_val = m_stream.get(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         int  operator *() const {  | ||||||
|  |             return m_val; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         void operator ++() {  | ||||||
|  |             m_val = m_stream.get(); | ||||||
|  |             if (m_val == '\n') ++m_line; | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         unsigned line() const { return m_line; } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     struct drat_record { | ||||||
|  |         enum tag_t { is_clause, is_node, is_bool_def }; | ||||||
|  |         tag_t            m_tag; | ||||||
|  |         // a clause populates m_lits and m_status
 | ||||||
|  |         // a node populates m_node_id, m_name, m_args
 | ||||||
|  |         // a bool def populates m_node_id and one element in m_args
 | ||||||
|  |         sat::literal_vector  m_lits; | ||||||
|  |         sat::status     m_status; | ||||||
|  |         unsigned        m_node_id; | ||||||
|  |         std::string     m_name; | ||||||
|  |         unsigned_vector m_args; | ||||||
|  |         drat_record(): | ||||||
|  |             m_tag(is_clause), | ||||||
|  |             m_status(sat::status::redundant()) | ||||||
|  |         {} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     std::ostream& operator<<(std::ostream& out, drat_record const& r); | ||||||
|  | 
 | ||||||
|  |     class drat_parser { | ||||||
|  |         dimacs::stream_buffer      in; | ||||||
|  |         std::ostream&      err; | ||||||
|  |         drat_record        m_record; | ||||||
|  |         std::function<int(char const*)> m_read_theory_id; | ||||||
|  |         svector<char>      m_buffer; | ||||||
|  | 
 | ||||||
|  |         char const* parse_sexpr(); | ||||||
|  |         char const* parse_identifier(); | ||||||
|  |         int read_theory_id(); | ||||||
|  |         bool next(); | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         drat_parser(std::istream & _in, std::ostream& err): | ||||||
|  |             in(_in), err(err) | ||||||
|  |         {} | ||||||
|  | 
 | ||||||
|  |         class iterator { | ||||||
|  |             drat_parser& p; | ||||||
|  |             bool m_eof; | ||||||
|  |         public: | ||||||
|  |             iterator(drat_parser& p, bool is_eof):p(p), m_eof(is_eof) { if (!m_eof) m_eof = !p.next(); } | ||||||
|  |             drat_record const& operator*() { return p.m_record; } | ||||||
|  |             iterator& operator++() { if (!p.next()) m_eof = true; return *this;} | ||||||
|  |             bool operator==(iterator const& other) const { return m_eof == other.m_eof; } | ||||||
|  |             bool operator!=(iterator const& other) const { return m_eof != other.m_eof; } | ||||||
|  |         }; | ||||||
|  |          | ||||||
|  |         iterator begin() { return iterator(*this, false); }; | ||||||
|  |         iterator end() { return iterator(*this, true); } | ||||||
|  | 
 | ||||||
|  |         void set_read_theory(std::function<int(char const*)>& r) { m_read_theory_id = r; } | ||||||
|  | 
 | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -69,11 +69,11 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|         // encodes one of var, and, !and, xor, !xor, ite, !ite.
 |         // encodes one of var, and, !and, xor, !xor, ite, !ite.
 | ||||||
|         class node { |         class node { | ||||||
|             bool     m_sign; |             bool     m_sign{ false }; | ||||||
|             bool_op  m_op; |             bool_op  m_op{ no_op }; | ||||||
|             uint64_t m_lut; |             uint64_t m_lut{ 0 }; | ||||||
|             unsigned m_size; |             unsigned m_size{ 0 }; | ||||||
|             unsigned m_offset; |             unsigned m_offset{ 0 }; | ||||||
|         public: |         public: | ||||||
|             node():  |             node():  | ||||||
|                 m_sign(false), m_op(no_op), m_size(UINT_MAX), m_offset(UINT_MAX) {} |                 m_sign(false), m_op(no_op), m_size(UINT_MAX), m_offset(UINT_MAX) {} | ||||||
|  |  | ||||||
|  | @ -38,13 +38,13 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|         solver&                              m_solver; |         solver&                              m_solver; | ||||||
|         scoped_ptr<solver>                   s; |         scoped_ptr<solver>                   s; | ||||||
|         unsigned                             m_bin_clauses; |         unsigned                             m_bin_clauses{ 0 }; | ||||||
|         unsigned                             m_stopped_at; |         unsigned                             m_stopped_at{ 0 }; | ||||||
|         vector<clause_vector>                m_use_list; |         vector<clause_vector>                m_use_list; | ||||||
|         unsigned                             m_limit1, m_limit2; |         unsigned                             m_limit1{ 0 }, m_limit2{ 0 }; | ||||||
|         bool_vector                        m_mark, m_mark2; |         bool_vector                        m_mark, m_mark2; | ||||||
|         literal_vector                       m_must_candidates, m_may_candidates; |         literal_vector                       m_must_candidates, m_may_candidates; | ||||||
|         unsigned                             m_state; |         unsigned                             m_state{ 0 }; | ||||||
| 
 | 
 | ||||||
|         void init_g() { m_state = 0x7; } |         void init_g() { m_state = 0x7; } | ||||||
|         void g_add_binary(literal l1, literal l2, bool flip2); |         void g_add_binary(literal l1, literal l2, bool flip2); | ||||||
|  |  | ||||||
|  | @ -29,14 +29,13 @@ namespace sat { | ||||||
|         m_out(nullptr), |         m_out(nullptr), | ||||||
|         m_bout(nullptr), |         m_bout(nullptr), | ||||||
|         m_inconsistent(false), |         m_inconsistent(false), | ||||||
|         m_num_add(0),  |  | ||||||
|         m_num_del(0), |  | ||||||
|         m_check_unsat(false), |         m_check_unsat(false), | ||||||
|         m_check_sat(false), |         m_check_sat(false), | ||||||
|         m_check(false), |         m_check(false), | ||||||
|         m_activity(false) |         m_activity(false) | ||||||
|     { |     { | ||||||
|         if (s.get_config().m_drat && s.get_config().m_drat_file != symbol()) { |         if (s.get_config().m_drat && s.get_config().m_drat_file.is_non_empty_string()) { | ||||||
|  |             std::cout << "DRAT " << s.get_config().m_drat_file << "\n"; | ||||||
|             auto mode = s.get_config().m_drat_binary ? (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc) : std::ios_base::out; |             auto mode = s.get_config().m_drat_binary ? (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc) : std::ios_base::out; | ||||||
|             m_out = alloc(std::ofstream, s.get_config().m_drat_file.str(), mode); |             m_out = alloc(std::ofstream, s.get_config().m_drat_file.str(), mode); | ||||||
|             if (s.get_config().m_drat_binary) { |             if (s.get_config().m_drat_binary) { | ||||||
|  | @ -75,6 +74,8 @@ namespace sat { | ||||||
|             out << "d"; |             out << "d"; | ||||||
|         else if (st.is_asserted()) |         else if (st.is_asserted()) | ||||||
|             out << "a"; |             out << "a"; | ||||||
|  |         else if (st.is_input()) | ||||||
|  |             out << "i"; | ||||||
| 
 | 
 | ||||||
|         if (!st.is_sat()) |         if (!st.is_sat()) | ||||||
|             out << " " << m_theory[st.get_th()]; |             out << " " << m_theory[st.get_th()]; | ||||||
|  | @ -84,7 +85,7 @@ namespace sat { | ||||||
|     void drat::dump(unsigned n, literal const* c, status st) { |     void drat::dump(unsigned n, literal const* c, status st) { | ||||||
|         if (st.is_asserted() && !s.m_ext) |         if (st.is_asserted() && !s.m_ext) | ||||||
|             return; |             return; | ||||||
|         if (m_activity && ((m_num_add % 1000) == 0))  |         if (m_activity && ((m_stats.m_num_add % 1000) == 0)) | ||||||
|             dump_activity(); |             dump_activity(); | ||||||
| 
 | 
 | ||||||
|         char buffer[10000]; |         char buffer[10000]; | ||||||
|  | @ -100,6 +101,14 @@ namespace sat { | ||||||
|             buffer[len++] = 'd'; |             buffer[len++] = 'd'; | ||||||
|             buffer[len++] = ' '; |             buffer[len++] = ' '; | ||||||
|         } |         } | ||||||
|  |         else if (st.is_input()) { | ||||||
|  |             buffer[len++] = 'i'; | ||||||
|  |             buffer[len++] = ' '; | ||||||
|  |         } | ||||||
|  |         else if (st.is_redundant() && !st.is_sat()) { | ||||||
|  |             buffer[len++] = 'r'; | ||||||
|  |             buffer[len++] = ' '; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if (!st.is_sat()) { |         if (!st.is_sat()) { | ||||||
|             for (char ch : m_theory[st.get_th()]) |             for (char ch : m_theory[st.get_th()]) | ||||||
|  | @ -164,8 +173,7 @@ namespace sat { | ||||||
|                     m_bout->write(buffer, len); |                     m_bout->write(buffer, len); | ||||||
|                     len = 0; |                     len = 0; | ||||||
|                 } |                 } | ||||||
|             } |             } while (v); | ||||||
|             while (v); |  | ||||||
|         } |         } | ||||||
|         buffer[len++] = 0; |         buffer[len++] = 0; | ||||||
|         m_bout->write(buffer, len); |         m_bout->write(buffer, len); | ||||||
|  | @ -198,7 +206,7 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|         declare(l); |         declare(l); | ||||||
|         IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); |         IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); | ||||||
|         if (st.is_redundant()) { |         if (st.is_redundant() && st.is_sat()) { | ||||||
|             verify(1, &l); |             verify(1, &l); | ||||||
|         } |         } | ||||||
|         if (st.is_deleted()) { |         if (st.is_deleted()) { | ||||||
|  | @ -223,7 +231,7 @@ namespace sat { | ||||||
|             // don't record binary as deleted.
 |             // don't record binary as deleted.
 | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             if (st.is_redundant()) { |             if (st.is_redundant() && st.is_sat()) { | ||||||
|                 verify(2, lits); |                 verify(2, lits); | ||||||
|             } |             } | ||||||
|             clause* c = m_alloc.mk_clause(2, lits, st.is_redundant()); |             clause* c = m_alloc.mk_clause(2, lits, st.is_redundant()); | ||||||
|  | @ -252,9 +260,9 @@ namespace sat { | ||||||
|             (*m_out) << "b " << v << " " << n << " 0\n"; |             (*m_out) << "b " << v << " " << n << " 0\n"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void drat::def_begin(unsigned n, symbol const& name) { |     void drat::def_begin(unsigned n, std::string const& name) { | ||||||
|         if (m_out) |         if (m_out) | ||||||
|             (*m_out) << "n " << n << " " << name; |             (*m_out) << "e " << n << " " << name; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void drat::def_add_arg(unsigned arg) { |     void drat::def_add_arg(unsigned arg) { | ||||||
|  | @ -300,7 +308,7 @@ namespace sat { | ||||||
|         unsigned n = c.size(); |         unsigned n = c.size(); | ||||||
|         IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); |         IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); | ||||||
| 
 | 
 | ||||||
|         if (st.is_redundant()) { |         if (st.is_redundant() && st.is_sat()) { | ||||||
|             verify(c); |             verify(c); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -435,17 +443,16 @@ namespace sat { | ||||||
|         } |         } | ||||||
|         m_units.shrink(num_units); |         m_units.shrink(num_units); | ||||||
|         bool ok = m_inconsistent; |         bool ok = m_inconsistent; | ||||||
|         // IF_VERBOSE(9, verbose_stream() << "is-drup " << m_inconsistent << "\n");
 |  | ||||||
|         m_inconsistent = false; |         m_inconsistent = false; | ||||||
| 
 |  | ||||||
|         return ok; |         return ok; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool drat::is_drat(unsigned n, literal const* c) { |     bool drat::is_drat(unsigned n, literal const* c) { | ||||||
|         if (m_inconsistent || n == 0) return true; |         if (m_inconsistent || n == 0)  | ||||||
|         for (unsigned i = 0; i < n; ++i) { |             return true; | ||||||
|             if (is_drat(n, c, i)) return true; |         for (unsigned i = 0; i < n; ++i)  | ||||||
|         } |             if (is_drat(n, c, i))  | ||||||
|  |                 return true; | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -499,7 +506,15 @@ namespace sat { | ||||||
|         for (unsigned i = 0; i < n; ++i) { |         for (unsigned i = 0; i < n; ++i) { | ||||||
|             declare(c[i]); |             declare(c[i]); | ||||||
|         } |         } | ||||||
|         if (!is_drup(n, c) && !is_drat(n, c)) { |         if (is_drup(n, c)) { | ||||||
|  |             ++m_stats.m_num_drup; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (is_drat(n, c)) { | ||||||
|  |             ++m_stats.m_num_drat; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         literal_vector lits(n, c); |         literal_vector lits(n, c); | ||||||
|         IF_VERBOSE(0, verbose_stream() << "Verification of " << lits << " failed\n"); |         IF_VERBOSE(0, verbose_stream() << "Verification of " << lits << " failed\n"); | ||||||
|         // s.display(std::cout);
 |         // s.display(std::cout);
 | ||||||
|  | @ -516,7 +531,6 @@ namespace sat { | ||||||
|               s.display(tout);); |               s.display(tout);); | ||||||
|         UNREACHABLE(); |         UNREACHABLE(); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     bool drat::contains(literal c, justification const& j) { |     bool drat::contains(literal c, justification const& j) { | ||||||
|         if (!m_check_sat) { |         if (!m_check_sat) { | ||||||
|  | @ -705,7 +719,7 @@ namespace sat { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void drat::add() { |     void drat::add() { | ||||||
|         ++m_num_add; |         ++m_stats.m_num_add; | ||||||
|         if (m_out) (*m_out) << "0\n"; |         if (m_out) (*m_out) << "0\n"; | ||||||
|         if (m_bout) bdump(0, nullptr, status::redundant()); |         if (m_bout) bdump(0, nullptr, status::redundant()); | ||||||
|         if (m_check_unsat) { |         if (m_check_unsat) { | ||||||
|  | @ -713,7 +727,7 @@ namespace sat { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     void drat::add(literal l, bool learned) { |     void drat::add(literal l, bool learned) { | ||||||
|         ++m_num_add; |         ++m_stats.m_num_add; | ||||||
|         status st = get_status(learned); |         status st = get_status(learned); | ||||||
|         if (m_out) dump(1, &l, st); |         if (m_out) dump(1, &l, st); | ||||||
|         if (m_bout) bdump(1, &l, st); |         if (m_bout) bdump(1, &l, st); | ||||||
|  | @ -721,9 +735,9 @@ namespace sat { | ||||||
|     } |     } | ||||||
|     void drat::add(literal l1, literal l2, status st) { |     void drat::add(literal l1, literal l2, status st) { | ||||||
|         if (st.is_deleted()) |         if (st.is_deleted()) | ||||||
|             ++m_num_del; |             ++m_stats.m_num_del; | ||||||
|         else |         else | ||||||
|             ++m_num_add; |             ++m_stats.m_num_add; | ||||||
|         literal ls[2] = { l1, l2 }; |         literal ls[2] = { l1, l2 }; | ||||||
|         if (m_out) dump(2, ls, st); |         if (m_out) dump(2, ls, st); | ||||||
|         if (m_bout) bdump(2, ls, st); |         if (m_bout) bdump(2, ls, st); | ||||||
|  | @ -731,9 +745,9 @@ namespace sat { | ||||||
|     } |     } | ||||||
|     void drat::add(clause& c, status st) { |     void drat::add(clause& c, status st) { | ||||||
|         if (st.is_deleted()) |         if (st.is_deleted()) | ||||||
|             ++m_num_del; |             ++m_stats.m_num_del; | ||||||
|         else |         else | ||||||
|             ++m_num_add; |             ++m_stats.m_num_add; | ||||||
|         if (m_out) dump(c.size(), c.begin(), st); |         if (m_out) dump(c.size(), c.begin(), st); | ||||||
|         if (m_bout) bdump(c.size(), c.begin(), st); |         if (m_bout) bdump(c.size(), c.begin(), st); | ||||||
|         if (m_check) { |         if (m_check) { | ||||||
|  | @ -743,9 +757,9 @@ namespace sat { | ||||||
|     } |     } | ||||||
|     void drat::add(literal_vector const& lits, status st) { |     void drat::add(literal_vector const& lits, status st) { | ||||||
|         if (st.is_deleted()) |         if (st.is_deleted()) | ||||||
|             ++m_num_del; |             ++m_stats.m_num_del; | ||||||
|         else |         else | ||||||
|             ++m_num_add; |             ++m_stats.m_num_add; | ||||||
|         if (m_check) { |         if (m_check) { | ||||||
|             switch (lits.size()) { |             switch (lits.size()) { | ||||||
|             case 0: add(); break; |             case 0: add(); break; | ||||||
|  | @ -761,7 +775,7 @@ namespace sat { | ||||||
|             dump(lits.size(), lits.c_ptr(), st); |             dump(lits.size(), lits.c_ptr(), st); | ||||||
|     } |     } | ||||||
|     void drat::add(literal_vector const& c) { |     void drat::add(literal_vector const& c) { | ||||||
|         ++m_num_add; |         ++m_stats.m_num_add; | ||||||
|         if (m_out) dump(c.size(), c.begin(), status::redundant()); |         if (m_out) dump(c.size(), c.begin(), status::redundant()); | ||||||
|         if (m_bout) bdump(c.size(), c.begin(), status::redundant()); |         if (m_bout) bdump(c.size(), c.begin(), status::redundant()); | ||||||
|         if (m_check) { |         if (m_check) { | ||||||
|  | @ -780,14 +794,14 @@ namespace sat { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void drat::del(literal l) { |     void drat::del(literal l) { | ||||||
|         ++m_num_del; |         ++m_stats.m_num_del; | ||||||
|         if (m_out) dump(1, &l, status::deleted()); |         if (m_out) dump(1, &l, status::deleted()); | ||||||
|         if (m_bout) bdump(1, &l, status::deleted()); |         if (m_bout) bdump(1, &l, status::deleted()); | ||||||
|         if (m_check_unsat) append(l, status::deleted()); |         if (m_check_unsat) append(l, status::deleted()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void drat::del(literal l1, literal l2) { |     void drat::del(literal l1, literal l2) { | ||||||
|         ++m_num_del; |         ++m_stats.m_num_del; | ||||||
|         literal ls[2] = { l1, l2 }; |         literal ls[2] = { l1, l2 }; | ||||||
|         if (m_out) dump(2, ls, status::deleted()); |         if (m_out) dump(2, ls, status::deleted()); | ||||||
|         if (m_bout) bdump(2, ls, status::deleted()); |         if (m_bout) bdump(2, ls, status::deleted()); | ||||||
|  | @ -806,7 +820,7 @@ namespace sat { | ||||||
|             m_seen[lit.index()] = false; |             m_seen[lit.index()] = false; | ||||||
|         } |         } | ||||||
| #endif | #endif | ||||||
|         ++m_num_del; |         ++m_stats.m_num_del; | ||||||
|         if (m_out) dump(c.size(), c.begin(), status::deleted()); |         if (m_out) dump(c.size(), c.begin(), status::deleted()); | ||||||
|         if (m_bout) bdump(c.size(), c.begin(), status::deleted()); |         if (m_bout) bdump(c.size(), c.begin(), status::deleted()); | ||||||
|         if (m_check) { |         if (m_check) { | ||||||
|  | @ -816,7 +830,7 @@ namespace sat { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void drat::del(literal_vector const& c) { |     void drat::del(literal_vector const& c) { | ||||||
|         ++m_num_del; |         ++m_stats.m_num_del; | ||||||
|         if (m_out) dump(c.size(), c.begin(), status::deleted()); |         if (m_out) dump(c.size(), c.begin(), status::deleted()); | ||||||
|         if (m_bout) bdump(c.size(), c.begin(), status::deleted()); |         if (m_bout) bdump(c.size(), c.begin(), status::deleted()); | ||||||
|         if (m_check) { |         if (m_check) { | ||||||
|  | @ -828,4 +842,25 @@ namespace sat { | ||||||
|     void drat::check_model(model const& m) { |     void drat::check_model(model const& m) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     std::ostream& operator<<(std::ostream& out, status const& st) { | ||||||
|  |         if (st.is_deleted()) | ||||||
|  |             out << "d"; | ||||||
|  |         else if (st.is_input()) | ||||||
|  |             out << "i"; | ||||||
|  |         else if (st.is_asserted()) | ||||||
|  |             out << "a"; | ||||||
|  |         else if (st.is_redundant() && !st.is_sat()) | ||||||
|  |             out << "r"; | ||||||
|  |         if (!st.is_sat()) | ||||||
|  |             out << " th" << st.m_orig; | ||||||
|  |         return out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void drat::collect_statistics(statistics& st) const { | ||||||
|  |         st.update("num-drup", m_stats.m_num_drup); | ||||||
|  |         st.update("num-drat", m_stats.m_num_drat); | ||||||
|  |         st.update("num-add", m_stats.m_num_add); | ||||||
|  |         st.update("num-del", m_stats.m_num_del); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,18 +19,24 @@ Notes: | ||||||
| 
 | 
 | ||||||
|     For SMT extensions are as follows: |     For SMT extensions are as follows: | ||||||
| 
 | 
 | ||||||
|     Assertion (trusted modulo internalizer): |     Input assertion: | ||||||
|  |       i <literal>* 0 | ||||||
|  | 
 | ||||||
|  |     Assertion (true modulo a theory): | ||||||
|       a [<theory-id>] <literal>* 0 |       a [<theory-id>] <literal>* 0 | ||||||
|     The optional theory id indicates the assertion is irredundant |     The if no theory id is given, the assertion is a tautology | ||||||
|  |     modulo Tseitin converison. Theory ids track whether the | ||||||
|  |     tautology is modulo a theory. | ||||||
|  |     Assertions are irredundant. | ||||||
| 
 | 
 | ||||||
|     Bridge from ast-node to boolean variable: |     Bridge from ast-node to boolean variable: | ||||||
|       b <bool-var-id> <ast-node-id> 0 |       b <bool-var-id> <ast-node-id> 0 | ||||||
| 
 | 
 | ||||||
|     Definition of an ast node: |     Definition of an expression (ast-node): | ||||||
|       n <ast-node-id> <name> <ast-node-id>* 0 |       e <ast-node-id> <name> <ast-node-id>* 0 | ||||||
| 
 | 
 | ||||||
|     Theory lemma |     Redundant clause (theory lemma if theory id is given) | ||||||
|       <theory-id> <literal>* 0 |       [r [<theory-id>]] <literal>* 0 | ||||||
| 
 | 
 | ||||||
|     Available theories are: |     Available theories are: | ||||||
|       - euf   The theory lemma should be a consequence of congruence closure. |       - euf   The theory lemma should be a consequence of congruence closure. | ||||||
|  | @ -45,8 +51,16 @@ Notes: | ||||||
| #include "sat_types.h" | #include "sat_types.h" | ||||||
| 
 | 
 | ||||||
| namespace sat { | namespace sat { | ||||||
|  |     class justification; | ||||||
|  |     class clause; | ||||||
|  | 
 | ||||||
|     class drat { |     class drat { | ||||||
|     private: |         struct stats { | ||||||
|  |             unsigned m_num_drup { 0 }; | ||||||
|  |             unsigned m_num_drat { 0 }; | ||||||
|  |             unsigned m_num_add { 0 }; | ||||||
|  |             unsigned m_num_del { 0 }; | ||||||
|  |         }; | ||||||
|         struct watched_clause { |         struct watched_clause { | ||||||
|             clause* m_clause; |             clause* m_clause; | ||||||
|             literal m_l1, m_l2; |             literal m_l1, m_l2; | ||||||
|  | @ -64,10 +78,10 @@ namespace sat { | ||||||
|         literal_vector          m_units; |         literal_vector          m_units; | ||||||
|         vector<watch>           m_watches; |         vector<watch>           m_watches; | ||||||
|         svector<lbool>          m_assignment; |         svector<lbool>          m_assignment; | ||||||
|         svector<std::string>    m_theory; |         vector<std::string>     m_theory; | ||||||
|         bool                    m_inconsistent; |         bool                    m_inconsistent; | ||||||
|         unsigned                m_num_add, m_num_del; |  | ||||||
|         bool                    m_check_unsat, m_check_sat, m_check, m_activity; |         bool                    m_check_unsat, m_check_sat, m_check, m_activity; | ||||||
|  |         stats                   m_stats; | ||||||
| 
 | 
 | ||||||
|         void dump_activity(); |         void dump_activity(); | ||||||
|         void dump(unsigned n, literal const* c, status st); |         void dump(unsigned n, literal const* c, status st); | ||||||
|  | @ -96,6 +110,7 @@ namespace sat { | ||||||
|         bool match(unsigned n, literal const* lits, clause const& c) const; |         bool match(unsigned n, literal const* lits, clause const& c) const; | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|  | 
 | ||||||
|         drat(solver& s); |         drat(solver& s); | ||||||
|         ~drat();   |         ~drat();   | ||||||
| 
 | 
 | ||||||
|  | @ -114,7 +129,7 @@ namespace sat { | ||||||
|         void bool_def(bool_var v, unsigned n); |         void bool_def(bool_var v, unsigned n); | ||||||
| 
 | 
 | ||||||
|         // declare AST node n with 'name' and arguments arg
 |         // declare AST node n with 'name' and arguments arg
 | ||||||
|         void def_begin(unsigned n, symbol const& name); |         void def_begin(unsigned n, std::string const& name); | ||||||
|         void def_add_arg(unsigned arg); |         void def_add_arg(unsigned arg); | ||||||
|         void def_end(); |         void def_end(); | ||||||
| 
 | 
 | ||||||
|  | @ -139,7 +154,11 @@ namespace sat { | ||||||
|         bool contains(literal c, justification const& j); |         bool contains(literal c, justification const& j); | ||||||
| 
 | 
 | ||||||
|         void check_model(model const& m); |         void check_model(model const& m); | ||||||
|  | 
 | ||||||
|  |         void collect_statistics(statistics& st) const; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     std::ostream& operator<<(std::ostream& out, status const& st); | ||||||
|  | 
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -236,7 +236,6 @@ namespace sat { | ||||||
|             if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) { |             if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) { | ||||||
|                 // cannot really eliminate v, since we have to notify extension of future assignments
 |                 // cannot really eliminate v, since we have to notify extension of future assignments
 | ||||||
|                 if (m_solver.m_config.m_drat && m_solver.m_config.m_drat_file.is_null()) { |                 if (m_solver.m_config.m_drat && m_solver.m_config.m_drat_file.is_null()) { | ||||||
|                     std::cout << "DRAT\n"; |  | ||||||
|                     m_solver.m_drat.add(~l, r, sat::status::redundant()); |                     m_solver.m_drat.add(~l, r, sat::status::redundant()); | ||||||
|                     m_solver.m_drat.add(l, ~r, sat::status::redundant()); |                     m_solver.m_drat.add(l, ~r, sat::status::redundant()); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -61,9 +61,10 @@ namespace sat { | ||||||
|         virtual void set_lookahead(lookahead* s) = 0; |         virtual void set_lookahead(lookahead* s) = 0; | ||||||
|         virtual void init_search() {} |         virtual void init_search() {} | ||||||
|         virtual bool propagate(literal l, ext_constraint_idx idx) = 0; |         virtual bool propagate(literal l, ext_constraint_idx idx) = 0; | ||||||
|  |         virtual bool unit_propagate() = 0; | ||||||
|         virtual bool is_external(bool_var v) = 0; |         virtual bool is_external(bool_var v) = 0; | ||||||
|         virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0; |         virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0; | ||||||
|         virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; |         virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) = 0; | ||||||
|         virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0; |         virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0; | ||||||
|         virtual void asserted(literal l) = 0; |         virtual void asserted(literal l) = 0; | ||||||
|         virtual check_result check() = 0; |         virtual check_result check() = 0; | ||||||
|  |  | ||||||
|  | @ -1900,7 +1900,6 @@ namespace sat { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool simplifier::try_eliminate(bool_var v) { |     bool simplifier::try_eliminate(bool_var v) { | ||||||
|         TRACE("sat_simplifier", tout << "processing: " << v << "\n";); |  | ||||||
|         if (value(v) != l_undef) |         if (value(v) != l_undef) | ||||||
|             return false; |             return false; | ||||||
| 
 | 
 | ||||||
|  | @ -1963,7 +1962,7 @@ namespace sat { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         TRACE("sat_simplifier", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); |         TRACE("sat_simplifier", tout << "eliminate " << v << ", before: " << before_clauses << " after: " << after_clauses << "\n";); | ||||||
|         m_elim_counter -= num_pos * num_neg + before_lits; |         m_elim_counter -= num_pos * num_neg + before_lits; | ||||||
| 
 | 
 | ||||||
|         m_elim_counter -= num_pos * num_neg + before_lits; |         m_elim_counter -= num_pos * num_neg + before_lits; | ||||||
|  |  | ||||||
|  | @ -86,7 +86,7 @@ namespace sat { | ||||||
|         m_cuber                   = nullptr; |         m_cuber                   = nullptr; | ||||||
|         m_local_search            = nullptr; |         m_local_search            = nullptr; | ||||||
|         m_mc.set_solver(this); |         m_mc.set_solver(this); | ||||||
|         //mk_var(false, false);
 |         mk_var(false, false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     solver::~solver() { |     solver::~solver() { | ||||||
|  | @ -138,7 +138,7 @@ namespace sat { | ||||||
|         m_qhead = 0; |         m_qhead = 0; | ||||||
|         m_trail.reset(); |         m_trail.reset(); | ||||||
|         m_scopes.reset(); |         m_scopes.reset(); | ||||||
|         //mk_var(false, false);
 |         mk_var(false, false); | ||||||
| 
 | 
 | ||||||
|         if (src.inconsistent()) { |         if (src.inconsistent()) { | ||||||
|             set_conflict(); |             set_conflict(); | ||||||
|  | @ -353,7 +353,7 @@ namespace sat { | ||||||
|     clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) { |     clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) { | ||||||
|         bool redundant = st.is_redundant(); |         bool redundant = st.is_redundant(); | ||||||
|         TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << (redundant?" learned":" aux") << "\n";); |         TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << (redundant?" learned":" aux") << "\n";); | ||||||
|         if (!redundant) { |         if (!redundant || !st.is_sat()) { | ||||||
|             unsigned old_sz = num_lits; |             unsigned old_sz = num_lits; | ||||||
|             bool keep = simplify_clause(num_lits, lits); |             bool keep = simplify_clause(num_lits, lits); | ||||||
|             TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); |             TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); | ||||||
|  | @ -1103,6 +1103,11 @@ namespace sat { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             wlist.set_end(it2); |             wlist.set_end(it2); | ||||||
|  |             if (m_ext) { | ||||||
|  |                 m_ext->unit_propagate(); | ||||||
|  |                 if (inconsistent()) | ||||||
|  |                     return false; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         SASSERT(m_qhead == m_trail.size()); |         SASSERT(m_qhead == m_trail.size()); | ||||||
|         SASSERT(!m_inconsistent); |         SASSERT(!m_inconsistent); | ||||||
|  | @ -1205,9 +1210,9 @@ namespace sat { | ||||||
|         } |         } | ||||||
|         try { |         try { | ||||||
|             init_search(); |             init_search(); | ||||||
|             if (inconsistent()) return l_false; |             if (check_inconsistent()) return l_false; | ||||||
|             propagate(false); |             propagate(false); | ||||||
|             if (inconsistent()) return l_false; |             if (check_inconsistent()) return l_false; | ||||||
|             init_assumptions(num_lits, lits); |             init_assumptions(num_lits, lits); | ||||||
|             propagate(false); |             propagate(false); | ||||||
|             if (check_inconsistent()) return l_false; |             if (check_inconsistent()) return l_false; | ||||||
|  | @ -1644,7 +1649,9 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|     bool solver::check_inconsistent() { |     bool solver::check_inconsistent() { | ||||||
|         if (inconsistent()) { |         if (inconsistent()) { | ||||||
|             if (tracking_assumptions()) |             if (tracking_assumptions() && at_search_lvl()) | ||||||
|  |                 resolve_conflict(); | ||||||
|  |             else if (m_config.m_drat && at_base_lvl()) | ||||||
|                 resolve_conflict(); |                 resolve_conflict(); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  | @ -2576,13 +2583,6 @@ namespace sat { | ||||||
|         if (m_step_size > m_config.m_step_size_min) { |         if (m_step_size > m_config.m_step_size_min) { | ||||||
|             m_step_size -= m_config.m_step_size_dec; |             m_step_size -= m_config.m_step_size_dec; | ||||||
|         } |         } | ||||||
|         struct reset_cache { |  | ||||||
|             solver& s; |  | ||||||
|             bool active; |  | ||||||
|             reset_cache(solver& s) :s(s), active(true) {} |  | ||||||
|             ~reset_cache() { if (active) s.m_cached_antecedent_js = 0; } |  | ||||||
|         }; |  | ||||||
|         reset_cache _reset(*this); |  | ||||||
|         bool unique_max; |         bool unique_max; | ||||||
|         m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max);         |         m_conflict_lvl = get_max_lvl(m_not_l, m_conflict, unique_max);         | ||||||
|         justification js = m_conflict; |         justification js = m_conflict; | ||||||
|  | @ -2609,7 +2609,6 @@ namespace sat { | ||||||
|             pop_reinit(m_scope_lvl - m_conflict_lvl + 1); |             pop_reinit(m_scope_lvl - m_conflict_lvl + 1); | ||||||
|             m_force_conflict_analysis = true; |             m_force_conflict_analysis = true; | ||||||
|             ++m_stats.m_backtracks; |             ++m_stats.m_backtracks; | ||||||
|             _reset.active = false; |  | ||||||
|             return l_undef; |             return l_undef; | ||||||
|         } |         } | ||||||
|         m_force_conflict_analysis = false; |         m_force_conflict_analysis = false; | ||||||
|  | @ -2685,7 +2684,7 @@ namespace sat { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             case justification::EXT_JUSTIFICATION: { |             case justification::EXT_JUSTIFICATION: { | ||||||
|                 fill_ext_antecedents(consequent, js); |                 fill_ext_antecedents(consequent, js, false); | ||||||
|                 for (literal l : m_ext_antecedents)  |                 for (literal l : m_ext_antecedents)  | ||||||
|                     process_antecedent(l, num_marks); |                     process_antecedent(l, num_marks); | ||||||
|                 break; |                 break; | ||||||
|  | @ -2786,6 +2785,7 @@ namespace sat { | ||||||
|         if (m_par && lemma) { |         if (m_par && lemma) { | ||||||
|             m_par->share_clause(*this, *lemma); |             m_par->share_clause(*this, *lemma); | ||||||
|         } |         } | ||||||
|  |         m_lemma.reset(); | ||||||
|         TRACE("sat_conflict_detail", tout << "consistent " << (!m_inconsistent) << " scopes: " << scope_lvl() << " backtrack: " << backtrack_lvl << " backjump: " << backjump_lvl << "\n";); |         TRACE("sat_conflict_detail", tout << "consistent " << (!m_inconsistent) << " scopes: " << scope_lvl() << " backtrack: " << backtrack_lvl << " backjump: " << backjump_lvl << "\n";); | ||||||
|         decay_activity(); |         decay_activity(); | ||||||
|         updt_phase_counters(); |         updt_phase_counters(); | ||||||
|  | @ -2847,7 +2847,7 @@ namespace sat { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case justification::EXT_JUSTIFICATION: { |         case justification::EXT_JUSTIFICATION: { | ||||||
|             fill_ext_antecedents(consequent, js); |             fill_ext_antecedents(consequent, js, true); | ||||||
|             for (literal l : m_ext_antecedents) { |             for (literal l : m_ext_antecedents) { | ||||||
|                 process_antecedent_for_unsat_core(l); |                 process_antecedent_for_unsat_core(l); | ||||||
|             } |             } | ||||||
|  | @ -2975,7 +2975,7 @@ namespace sat { | ||||||
|         case justification::EXT_JUSTIFICATION: |         case justification::EXT_JUSTIFICATION: | ||||||
|             if (not_l != null_literal)  |             if (not_l != null_literal)  | ||||||
|                 not_l.neg(); |                 not_l.neg(); | ||||||
|             fill_ext_antecedents(not_l, js); |             fill_ext_antecedents(not_l, js, true); | ||||||
|             for (literal l : m_ext_antecedents)  |             for (literal l : m_ext_antecedents)  | ||||||
|                 level = update_max_level(l, level, unique_max); |                 level = update_max_level(l, level, unique_max); | ||||||
|             return level; |             return level; | ||||||
|  | @ -3031,16 +3031,12 @@ namespace sat { | ||||||
|     /**
 |     /**
 | ||||||
|        \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. |        \brief js is an external justification. Collect its antecedents and store at m_ext_antecedents. | ||||||
|     */ |     */ | ||||||
|     void solver::fill_ext_antecedents(literal consequent, justification js) { |     void solver::fill_ext_antecedents(literal consequent, justification js, bool probing) { | ||||||
|         SASSERT(js.is_ext_justification()); |         SASSERT(js.is_ext_justification()); | ||||||
|         SASSERT(m_ext); |         SASSERT(m_ext); | ||||||
|         auto idx = js.get_ext_justification_idx(); |         auto idx = js.get_ext_justification_idx(); | ||||||
|         if (consequent == m_cached_antecedent_consequent && idx == m_cached_antecedent_js) |  | ||||||
|             return; |  | ||||||
|         m_ext_antecedents.reset(); |         m_ext_antecedents.reset(); | ||||||
|         m_ext->get_antecedents(consequent, idx, m_ext_antecedents); |         m_ext->get_antecedents(consequent, idx, m_ext_antecedents, probing); | ||||||
|         m_cached_antecedent_consequent = consequent; |  | ||||||
|         m_cached_antecedent_js = idx; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::is_two_phase() const { |     bool solver::is_two_phase() const { | ||||||
|  | @ -3354,7 +3350,7 @@ namespace sat { | ||||||
|             } |             } | ||||||
|             case justification::EXT_JUSTIFICATION: { |             case justification::EXT_JUSTIFICATION: { | ||||||
|                 literal consequent(var, value(var) == l_false); |                 literal consequent(var, value(var) == l_false); | ||||||
|                 fill_ext_antecedents(consequent, js); |                 fill_ext_antecedents(consequent, js, false); | ||||||
|                 for (literal l : m_ext_antecedents) { |                 for (literal l : m_ext_antecedents) { | ||||||
|                     if (!process_antecedent_for_minimization(l)) { |                     if (!process_antecedent_for_minimization(l)) { | ||||||
|                         reset_unmark(old_size); |                         reset_unmark(old_size); | ||||||
|  | @ -3496,7 +3492,7 @@ namespace sat { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             case justification::EXT_JUSTIFICATION: { |             case justification::EXT_JUSTIFICATION: { | ||||||
|                 fill_ext_antecedents(~m_lemma[i], js); |                 fill_ext_antecedents(~m_lemma[i], js, true); | ||||||
|                 for (literal l : m_ext_antecedents) { |                 for (literal l : m_ext_antecedents) { | ||||||
|                     update_lrb_reasoned(l); |                     update_lrb_reasoned(l); | ||||||
|                 } |                 } | ||||||
|  | @ -3657,6 +3653,7 @@ namespace sat { | ||||||
|         s.m_trail_lim = m_trail.size(); |         s.m_trail_lim = m_trail.size(); | ||||||
|         s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size(); |         s.m_clauses_to_reinit_lim = m_clauses_to_reinit.size(); | ||||||
|         s.m_inconsistent = m_inconsistent; |         s.m_inconsistent = m_inconsistent; | ||||||
|  |         // m_vars_lim.push(num_vars());
 | ||||||
|         if (m_ext) |         if (m_ext) | ||||||
|             m_ext->push(); |             m_ext->push(); | ||||||
|     } |     } | ||||||
|  | @ -3668,11 +3665,46 @@ namespace sat { | ||||||
|         m_stats.m_units = init_trail_size(); |         m_stats.m_units = init_trail_size(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void solver::pop_vars(unsigned num_scopes) { | ||||||
|  |         unsigned old_num_vars = m_vars_lim.pop(num_scopes); | ||||||
|  |         if (old_num_vars == num_vars())  | ||||||
|  |             return; | ||||||
|  |         IF_VERBOSE(0, verbose_stream() << "new variables created under scope\n";); | ||||||
|  |         for (unsigned v = old_num_vars; v < num_vars(); ++v) { | ||||||
|  |              | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::shrink_vars(unsigned v) { | ||||||
|  |         for (bool_var i = v; i < m_justification.size(); ++i) { | ||||||
|  |             m_case_split_queue.del_var_eh(i); | ||||||
|  |             m_probing.reset_cache(literal(i, true)); | ||||||
|  |             m_probing.reset_cache(literal(i, false)); | ||||||
|  |         } | ||||||
|  |         m_watches.shrink(2*v); | ||||||
|  |         m_assignment.shrink(2*v); | ||||||
|  |         m_justification.shrink(v); | ||||||
|  |         m_decision.shrink(v); | ||||||
|  |         m_eliminated.shrink(v); | ||||||
|  |         m_external.shrink(v); | ||||||
|  |         m_touched.shrink(v); | ||||||
|  |         m_activity.shrink(v); | ||||||
|  |         m_mark.shrink(v); | ||||||
|  |         m_lit_mark.shrink(2*v); | ||||||
|  |         m_phase.shrink(v); | ||||||
|  |         m_best_phase.shrink(v); | ||||||
|  |         m_prev_phase.shrink(v); | ||||||
|  |         m_assigned_since_gc.shrink(v); | ||||||
|  |         m_simplifier.reset_todos(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void solver::pop(unsigned num_scopes) { |     void solver::pop(unsigned num_scopes) { | ||||||
|         if (num_scopes == 0) |         if (num_scopes == 0) | ||||||
|             return; |             return; | ||||||
|         if (m_ext) |         if (m_ext) { | ||||||
|  |             // pop_vars(num_scopes);
 | ||||||
|             m_ext->pop(num_scopes); |             m_ext->pop(num_scopes); | ||||||
|  |         } | ||||||
|         SASSERT(num_scopes <= scope_lvl()); |         SASSERT(num_scopes <= scope_lvl()); | ||||||
|         unsigned new_lvl = scope_lvl() - num_scopes; |         unsigned new_lvl = scope_lvl() - num_scopes; | ||||||
|         scope & s        = m_scopes[new_lvl]; |         scope & s        = m_scopes[new_lvl]; | ||||||
|  | @ -3709,12 +3741,27 @@ namespace sat { | ||||||
|         m_qhead = m_trail.size(); |         m_qhead = m_trail.size(); | ||||||
|         if (!m_replay_assign.empty()) IF_VERBOSE(20, verbose_stream() << "replay assign: " << m_replay_assign.size() << "\n"); |         if (!m_replay_assign.empty()) IF_VERBOSE(20, verbose_stream() << "replay assign: " << m_replay_assign.size() << "\n"); | ||||||
|         for (unsigned i = m_replay_assign.size(); i-- > 0; ) { |         for (unsigned i = m_replay_assign.size(); i-- > 0; ) { | ||||||
|             m_trail.push_back(m_replay_assign[i]);             |             literal lit = m_replay_assign[i]; | ||||||
|  |             if (m_ext && m_external[lit.var()]) | ||||||
|  |                 m_ext->asserted(lit); | ||||||
|  |             m_trail.push_back(lit);             | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         m_replay_assign.reset(); |         m_replay_assign.reset(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void solver::get_reinit_literals(unsigned n, literal_vector& r) { | ||||||
|  |         unsigned new_lvl = scope_lvl() - n; | ||||||
|  |         unsigned old_sz = m_scopes[new_lvl].m_clauses_to_reinit_lim; | ||||||
|  |         for (unsigned i = m_clauses_to_reinit.size(); i-- > old_sz; ) { | ||||||
|  |             clause_wrapper cw = m_clauses_to_reinit[i]; | ||||||
|  |             for (unsigned j = cw.size(); j-- > 0; ) | ||||||
|  |                 r.push_back(cw[j]); | ||||||
|  |         } | ||||||
|  |         for (literal lit : m_lemma) | ||||||
|  |             r.push_back(lit); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void solver::reinit_clauses(unsigned old_sz) { |     void solver::reinit_clauses(unsigned old_sz) { | ||||||
|         unsigned sz = m_clauses_to_reinit.size(); |         unsigned sz = m_clauses_to_reinit.size(); | ||||||
|         SASSERT(old_sz <= sz); |         SASSERT(old_sz <= sz); | ||||||
|  | @ -3834,26 +3881,7 @@ namespace sat { | ||||||
|          |          | ||||||
|         // v is an index of a variable that does not occur in solver state.
 |         // v is an index of a variable that does not occur in solver state.
 | ||||||
|         if (v < m_justification.size()) { |         if (v < m_justification.size()) { | ||||||
|             for (bool_var i = v; i < m_justification.size(); ++i) { |             shrink_vars(v); | ||||||
|                 m_case_split_queue.del_var_eh(i); |  | ||||||
|                 m_probing.reset_cache(literal(i, true)); |  | ||||||
|                 m_probing.reset_cache(literal(i, false)); |  | ||||||
|             } |  | ||||||
|             m_watches.shrink(2*v); |  | ||||||
|             m_assignment.shrink(2*v); |  | ||||||
|             m_justification.shrink(v); |  | ||||||
|             m_decision.shrink(v); |  | ||||||
|             m_eliminated.shrink(v); |  | ||||||
|             m_external.shrink(v); |  | ||||||
|             m_touched.shrink(v); |  | ||||||
|             m_activity.shrink(v); |  | ||||||
|             m_mark.shrink(v); |  | ||||||
|             m_lit_mark.shrink(2*v); |  | ||||||
|             m_phase.shrink(v); |  | ||||||
|             m_best_phase.shrink(v); |  | ||||||
|             m_prev_phase.shrink(v); |  | ||||||
|             m_assigned_since_gc.shrink(v); |  | ||||||
|             m_simplifier.reset_todos(); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -4766,7 +4794,7 @@ namespace sat { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case justification::EXT_JUSTIFICATION: { |         case justification::EXT_JUSTIFICATION: { | ||||||
|             fill_ext_antecedents(lit, js); |             fill_ext_antecedents(lit, js, true); | ||||||
|             for (literal l : m_ext_antecedents) { |             for (literal l : m_ext_antecedents) { | ||||||
|                 if (check_domain(lit, l) && all_found) { |                 if (check_domain(lit, l) && all_found) { | ||||||
|                     s |= m_antecedents.find(l.var()); |                     s |= m_antecedents.find(l.var()); | ||||||
|  |  | ||||||
|  | @ -45,6 +45,7 @@ Revision History: | ||||||
| #include "util/trace.h" | #include "util/trace.h" | ||||||
| #include "util/rlimit.h" | #include "util/rlimit.h" | ||||||
| #include "util/scoped_ptr_vector.h" | #include "util/scoped_ptr_vector.h" | ||||||
|  | #include "util/scoped_limit_trail.h" | ||||||
| 
 | 
 | ||||||
| namespace sat { | namespace sat { | ||||||
| 
 | 
 | ||||||
|  | @ -172,6 +173,7 @@ namespace sat { | ||||||
|             bool     m_inconsistent; |             bool     m_inconsistent; | ||||||
|         }; |         }; | ||||||
|         svector<scope>          m_scopes; |         svector<scope>          m_scopes; | ||||||
|  |         scoped_limit_trail      m_vars_lim; | ||||||
|         stopwatch               m_stopwatch; |         stopwatch               m_stopwatch; | ||||||
|         params_ref              m_params; |         params_ref              m_params; | ||||||
|         scoped_ptr<solver>      m_clone; // for debugging purposes
 |         scoped_ptr<solver>      m_clone; // for debugging purposes
 | ||||||
|  | @ -399,6 +401,7 @@ namespace sat { | ||||||
|         bool canceled() { return !m_rlimit.inc(); } |         bool canceled() { return !m_rlimit.inc(); } | ||||||
|         config const& get_config() const { return m_config; } |         config const& get_config() const { return m_config; } | ||||||
|         drat& get_drat() { return m_drat; } |         drat& get_drat() { return m_drat; } | ||||||
|  |         drat* get_drat_ptr() override { return &m_drat;  } | ||||||
|         void set_incremental(bool b) { m_config.m_incremental = b; } |         void set_incremental(bool b) { m_config.m_incremental = b; } | ||||||
|         bool is_incremental() const { return m_config.m_incremental; } |         bool is_incremental() const { return m_config.m_incremental; } | ||||||
|         extension* get_extension() const override { return m_ext.get(); } |         extension* get_extension() const override { return m_ext.get(); } | ||||||
|  | @ -565,8 +568,6 @@ namespace sat { | ||||||
|         unsigned       m_conflict_lvl; |         unsigned       m_conflict_lvl; | ||||||
|         literal_vector m_lemma; |         literal_vector m_lemma; | ||||||
|         literal_vector m_ext_antecedents; |         literal_vector m_ext_antecedents; | ||||||
|         literal        m_cached_antecedent_consequent; |  | ||||||
|         ext_justification_idx m_cached_antecedent_js { 0 }; |  | ||||||
|         bool use_backjumping(unsigned num_scopes); |         bool use_backjumping(unsigned num_scopes); | ||||||
|         bool resolve_conflict(); |         bool resolve_conflict(); | ||||||
|         lbool resolve_conflict_core(); |         lbool resolve_conflict_core(); | ||||||
|  | @ -582,7 +583,7 @@ namespace sat { | ||||||
|         void resolve_conflict_for_unsat_core(); |         void resolve_conflict_for_unsat_core(); | ||||||
|         void process_antecedent_for_unsat_core(literal antecedent); |         void process_antecedent_for_unsat_core(literal antecedent); | ||||||
|         void process_consequent_for_unsat_core(literal consequent, justification const& js); |         void process_consequent_for_unsat_core(literal consequent, justification const& js); | ||||||
|         void fill_ext_antecedents(literal consequent, justification js); |         void fill_ext_antecedents(literal consequent, justification js, bool probing); | ||||||
|         unsigned skip_literals_above_conflict_level(); |         unsigned skip_literals_above_conflict_level(); | ||||||
|         void updt_phase_of_vars(); |         void updt_phase_of_vars(); | ||||||
|         void updt_phase_counters(); |         void updt_phase_counters(); | ||||||
|  | @ -621,6 +622,8 @@ namespace sat { | ||||||
|         void push(); |         void push(); | ||||||
|         void pop(unsigned num_scopes); |         void pop(unsigned num_scopes); | ||||||
|         void pop_reinit(unsigned num_scopes); |         void pop_reinit(unsigned num_scopes); | ||||||
|  |         void shrink_vars(unsigned v); | ||||||
|  |         void pop_vars(unsigned num_scopes); | ||||||
| 
 | 
 | ||||||
|         void unassign_vars(unsigned old_sz, unsigned new_lvl); |         void unassign_vars(unsigned old_sz, unsigned new_lvl); | ||||||
|         void reinit_clauses(unsigned old_sz); |         void reinit_clauses(unsigned old_sz); | ||||||
|  | @ -635,6 +638,14 @@ namespace sat { | ||||||
|         bool_var max_var(clause_vector& clauses, bool_var v); |         bool_var max_var(clause_vector& clauses, bool_var v); | ||||||
|         bool_var max_var(bool learned, bool_var v); |         bool_var max_var(bool learned, bool_var v); | ||||||
| 
 | 
 | ||||||
|  |         // -----------------------
 | ||||||
|  |         //
 | ||||||
|  |         // External
 | ||||||
|  |         //
 | ||||||
|  |         // -----------------------
 | ||||||
|  |     public: | ||||||
|  |         void set_should_simplify() { m_next_simplify = m_conflicts_since_init; } | ||||||
|  |         void get_reinit_literals(unsigned num_scopes, literal_vector& r); | ||||||
|     public: |     public: | ||||||
|         void user_push() override; |         void user_push() override; | ||||||
|         void user_pop(unsigned num_scopes) override; |         void user_pop(unsigned num_scopes) override; | ||||||
|  | @ -798,4 +809,3 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|     std::ostream & operator<<(std::ostream & out, mk_stat const & stat); |     std::ostream & operator<<(std::ostream & out, mk_stat const & stat); | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -126,7 +126,7 @@ public: | ||||||
|         auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension()); |         auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension()); | ||||||
|         if (ext) { |         if (ext) { | ||||||
|             auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep2asm, is_incremental()); |             auto& si = result->m_goal2sat.si(dst_m, m_params, result->m_solver, result->m_map, result->m_dep2asm, is_incremental()); | ||||||
|             euf::solver::scoped_set_translate st(*ext, dst_m, result->m_map, si);   |             euf::solver::scoped_set_translate st(*ext, dst_m, si);   | ||||||
|             result->m_solver.copy(m_solver); |             result->m_solver.copy(m_solver); | ||||||
|         }         |         }         | ||||||
|         else { |         else { | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|     class cut_simplifier; |     class cut_simplifier; | ||||||
|     class extension; |     class extension; | ||||||
|  |     class drat; | ||||||
| 
 | 
 | ||||||
|     class solver_core { |     class solver_core { | ||||||
|     protected: |     protected: | ||||||
|  | @ -95,6 +96,8 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|         virtual cut_simplifier* get_cut_simplifier() { return nullptr; } |         virtual cut_simplifier* get_cut_simplifier() { return nullptr; } | ||||||
| 
 | 
 | ||||||
|  |         virtual drat* get_drat_ptr() { return nullptr;  } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|         // The following methods are used when converting the state from the SAT solver back
 |         // The following methods are used when converting the state from the SAT solver back
 | ||||||
|         // to a set of assertions. 
 |         // to a set of assertions. 
 | ||||||
|  |  | ||||||
|  | @ -254,19 +254,22 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|     class status { |     class status { | ||||||
|     public: |     public: | ||||||
|         enum class st { asserted, redundant, deleted }; |         enum class st { input, asserted, redundant, deleted }; | ||||||
|         st m_st; |         st m_st; | ||||||
|         int m_orig; |         int m_orig; | ||||||
|     public: |     public: | ||||||
|         status(enum st s, int o) : m_st(s), m_orig(o) {}; |         status(enum st s, int o) : m_st(s), m_orig(o) {}; | ||||||
|         status(status const& s) : m_st(s.m_st), m_orig(s.m_orig) {} |         status(status const& s) : m_st(s.m_st), m_orig(s.m_orig) {} | ||||||
|         status(status&& s) noexcept { m_st = st::asserted; m_orig = -1; std::swap(m_st, s.m_st); std::swap(m_orig, s.m_orig); } |         status(status&& s) noexcept { m_st = st::asserted; m_orig = -1; std::swap(m_st, s.m_st); std::swap(m_orig, s.m_orig); } | ||||||
|  |         status& operator=(status const& other) { m_st = other.m_st; m_orig = other.m_orig; return *this; } | ||||||
|         static status redundant() { return status(status::st::redundant, -1); } |         static status redundant() { return status(status::st::redundant, -1); } | ||||||
|         static status asserted() { return status(status::st::asserted, -1); } |         static status asserted() { return status(status::st::asserted, -1); } | ||||||
|         static status deleted() { return status(status::st::deleted, -1); } |         static status deleted() { return status(status::st::deleted, -1); } | ||||||
|  |         static status input() { return status(status::st::input, -1); } | ||||||
| 
 | 
 | ||||||
|         static status th(bool redundant, int id) { return status(redundant ? st::redundant : st::asserted, id); } |         static status th(bool redundant, int id) { return status(redundant ? st::redundant : st::asserted, id); } | ||||||
| 
 | 
 | ||||||
|  |         bool is_input() const { return st::input == m_st; } | ||||||
|         bool is_redundant() const { return st::redundant == m_st; } |         bool is_redundant() const { return st::redundant == m_st; } | ||||||
|         bool is_asserted() const { return st::asserted == m_st; } |         bool is_asserted() const { return st::asserted == m_st; } | ||||||
|         bool is_deleted() const { return st::deleted == m_st; } |         bool is_deleted() const { return st::deleted == m_st; } | ||||||
|  | @ -275,6 +278,5 @@ namespace sat { | ||||||
|         int  get_th() const { return m_orig;  } |         int  get_th() const { return m_orig;  } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,10 +3,12 @@ z3_add_component(sat_smt | ||||||
|     atom2bool_var.cpp |     atom2bool_var.cpp | ||||||
|     ba_internalize.cpp |     ba_internalize.cpp | ||||||
|     ba_solver.cpp |     ba_solver.cpp | ||||||
|  |     bv_ackerman.cpp | ||||||
|     bv_internalize.cpp |     bv_internalize.cpp | ||||||
|     bv_solver.cpp |     bv_solver.cpp | ||||||
|     euf_ackerman.cpp |     euf_ackerman.cpp | ||||||
|     euf_internalize.cpp |     euf_internalize.cpp | ||||||
|  |     euf_invariant.cpp | ||||||
|     euf_model.cpp |     euf_model.cpp | ||||||
|     euf_proof.cpp |     euf_proof.cpp | ||||||
|     euf_solver.cpp |     euf_solver.cpp | ||||||
|  |  | ||||||
|  | @ -21,6 +21,10 @@ Author: | ||||||
| 
 | 
 | ||||||
| namespace sat { | namespace sat { | ||||||
| 
 | 
 | ||||||
|  |     void ba_solver::internalize(expr* e, bool redundant) { | ||||||
|  |         internalize(e, false, false, redundant); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     literal ba_solver::internalize(expr* e, bool sign, bool root, bool redundant) { |     literal ba_solver::internalize(expr* e, bool sign, bool root, bool redundant) { | ||||||
|         flet<bool> _redundant(m_is_redundant, redundant); |         flet<bool> _redundant(m_is_redundant, redundant); | ||||||
|         if (m_pb.is_pb(e))  |         if (m_pb.is_pb(e))  | ||||||
|  |  | ||||||
|  | @ -2107,8 +2107,8 @@ namespace sat { | ||||||
|     // ----------------------------
 |     // ----------------------------
 | ||||||
|     // constraint generic methods
 |     // constraint generic methods
 | ||||||
| 
 | 
 | ||||||
|     void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) { |     void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) { | ||||||
|         get_antecedents(l, index2constraint(idx), r); |         get_antecedents(l, index2constraint(idx), r, probing); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool ba_solver::is_watched(literal lit, constraint const& c) const { |     bool ba_solver::is_watched(literal lit, constraint const& c) const { | ||||||
|  | @ -2128,14 +2128,14 @@ namespace sat { | ||||||
|         get_wlist(~lit).push_back(w); |         get_wlist(~lit).push_back(w); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r) { |     void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r, bool probing) { | ||||||
|         switch (c.tag()) { |         switch (c.tag()) { | ||||||
|         case card_t: get_antecedents(l, c.to_card(), r); break; |         case card_t: get_antecedents(l, c.to_card(), r); break; | ||||||
|         case pb_t: get_antecedents(l, c.to_pb(), r); break; |         case pb_t: get_antecedents(l, c.to_pb(), r); break; | ||||||
|         case xr_t: get_antecedents(l, c.to_xr(), r); break; |         case xr_t: get_antecedents(l, c.to_xr(), r); break; | ||||||
|         default: UNREACHABLE(); break;             |         default: UNREACHABLE(); break;             | ||||||
|         } |         } | ||||||
|         if (get_config().m_drat && m_solver) { |         if (get_config().m_drat && m_solver && !probing) { | ||||||
|             literal_vector lits; |             literal_vector lits; | ||||||
|             for (literal lit : r)  |             for (literal lit : r)  | ||||||
|                 lits.push_back(~lit); |                 lits.push_back(~lit); | ||||||
|  |  | ||||||
|  | @ -361,7 +361,7 @@ namespace sat { | ||||||
|         void set_conflict(constraint& c, literal lit); |         void set_conflict(constraint& c, literal lit); | ||||||
|         void assign(constraint& c, literal lit); |         void assign(constraint& c, literal lit); | ||||||
|         bool assigned_above(literal above, literal below); |         bool assigned_above(literal above, literal below); | ||||||
|         void get_antecedents(literal l, constraint const& c, literal_vector & r); |         void get_antecedents(literal l, constraint const& c, literal_vector & r, bool probing); | ||||||
|         bool validate_conflict(constraint const& c) const; |         bool validate_conflict(constraint const& c) const; | ||||||
|         bool validate_unit_propagation(constraint const& c, literal alit) const; |         bool validate_unit_propagation(constraint const& c, literal alit) const; | ||||||
|         void validate_eliminated(); |         void validate_eliminated(); | ||||||
|  | @ -576,8 +576,9 @@ namespace sat { | ||||||
| 
 | 
 | ||||||
|         bool is_external(bool_var v) override; |         bool is_external(bool_var v) override; | ||||||
|         bool propagate(literal l, ext_constraint_idx idx) override; |         bool propagate(literal l, ext_constraint_idx idx) override; | ||||||
|  |         bool unit_propagate() override { return false; } | ||||||
|         lbool resolve_conflict() override; |         lbool resolve_conflict() override; | ||||||
|         void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override; |         void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) override; | ||||||
|         void asserted(literal l) override; |         void asserted(literal l) override; | ||||||
|         check_result check() override; |         check_result check() override; | ||||||
|         void push() override; |         void push() override; | ||||||
|  | @ -604,6 +605,7 @@ namespace sat { | ||||||
|         bool check_model(model const& m) const override; |         bool check_model(model const& m) const override; | ||||||
| 
 | 
 | ||||||
|         literal internalize(expr* e, bool sign, bool root, bool redundant) override; |         literal internalize(expr* e, bool sign, bool root, bool redundant) override; | ||||||
|  |         void internalize(expr* e, bool redundant) override; | ||||||
|         bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override; |         bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override; | ||||||
|         euf::th_solver* fresh(solver* s, euf::solver& ctx) override; |         euf::th_solver* fresh(solver* s, euf::solver& ctx) override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										153
									
								
								src/sat/smt/bv_ackerman.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/sat/smt/bv_ackerman.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2020 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     bv_ackerman.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Ackerman reduction plugin for bit-vectors | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2020-08-28 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "sat/smt/bv_solver.h" | ||||||
|  | #include "sat/smt/bv_ackerman.h" | ||||||
|  | 
 | ||||||
|  | namespace bv { | ||||||
|  | 
 | ||||||
|  |     ackerman::ackerman(solver& s): s(s) { | ||||||
|  |         new_tmp(); | ||||||
|  |         m_propagate_low_watermark = s.get_config().m_dack_threshold; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ackerman::~ackerman() { | ||||||
|  |         reset(); | ||||||
|  |         dealloc(m_tmp_vv); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::reset() { | ||||||
|  |         m_table.reset(); | ||||||
|  |         m_queue = nullptr;         | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::used_eq_eh(euf::theory_var v1, euf::theory_var v2) { | ||||||
|  |         if (v1 == v2) | ||||||
|  |             return; | ||||||
|  |         if (v1 > v2) | ||||||
|  |             std::swap(v1, v2); | ||||||
|  |         vv* n = m_tmp_vv; | ||||||
|  |         n->v1 = v1; | ||||||
|  |         n->v2 = v2; | ||||||
|  |         vv* other = m_table.insert_if_not_there(n);         | ||||||
|  |         other->m_count++; | ||||||
|  |         update_glue(*other); | ||||||
|  |         if (other->m_count > m_propagate_high_watermark || other->m_glue == 0) | ||||||
|  |             s.s().set_should_simplify(); | ||||||
|  |         vv::push_to_front(m_queue, other); | ||||||
|  |         if (other == n) { | ||||||
|  |             new_tmp();         | ||||||
|  |             gc(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::update_glue(vv& v) { | ||||||
|  |         unsigned sz = s.m_bits[v.v1].size(); | ||||||
|  |         m_diff_levels.reserve(s.s().scope_lvl() + 1, false); | ||||||
|  |         unsigned glue = 0; | ||||||
|  |         unsigned max_glue = v.m_glue; | ||||||
|  |         auto const& bitsa = s.m_bits[v.v1]; | ||||||
|  |         auto const& bitsb = s.m_bits[v.v2]; | ||||||
|  |         unsigned i = 0; | ||||||
|  |         for (; i < sz && i < max_glue; ++i) { | ||||||
|  |             sat::literal a = bitsa[i]; | ||||||
|  |             sat::literal b = bitsb[i]; | ||||||
|  |             if (a == b) | ||||||
|  |                 continue; | ||||||
|  |             SASSERT(s.s().value(a) != l_undef); | ||||||
|  |             SASSERT(s.s().value(b) == s.s().value(a)); | ||||||
|  |             unsigned lvl_a = s.s().lvl(a); | ||||||
|  |             unsigned lvl_b = s.s().lvl(b); | ||||||
|  |             if (!m_diff_levels[lvl_a]) { | ||||||
|  |                 m_diff_levels[lvl_a] = true; | ||||||
|  |                 ++glue; | ||||||
|  |             } | ||||||
|  |             if (!m_diff_levels[lvl_b]) { | ||||||
|  |                 m_diff_levels[lvl_b] = true; | ||||||
|  |                 ++glue; | ||||||
|  |             }             | ||||||
|  |         } | ||||||
|  |         for (; i-- > 0; ) { | ||||||
|  |             sat::literal a = bitsa[i]; | ||||||
|  |             sat::literal b = bitsb[i]; | ||||||
|  |             if (a != b) { | ||||||
|  |                 m_diff_levels[s.s().lvl(a)] = false; | ||||||
|  |                 m_diff_levels[s.s().lvl(b)] = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (glue < max_glue)  | ||||||
|  |             v.m_glue = glue <= sz ? 0 : glue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::remove(vv* p) { | ||||||
|  |         vv::remove_from(m_queue, p); | ||||||
|  |         m_table.erase(p); | ||||||
|  |         dealloc(p); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::new_tmp() { | ||||||
|  |         m_tmp_vv = alloc(vv); | ||||||
|  |         m_tmp_vv->init(m_tmp_vv); | ||||||
|  |         m_tmp_vv->m_count = 0; | ||||||
|  |         m_tmp_vv->m_glue = UINT_MAX; | ||||||
|  |     } | ||||||
|  |          | ||||||
|  |     void ackerman::gc() { | ||||||
|  |         m_num_propagations_since_last_gc++; | ||||||
|  |         if (m_num_propagations_since_last_gc <= s.get_config().m_dack_gc)  | ||||||
|  |             return; | ||||||
|  |         m_num_propagations_since_last_gc = 0; | ||||||
|  |          | ||||||
|  |         while (m_table.size() > m_gc_threshold)  | ||||||
|  |             remove(m_queue->prev());      | ||||||
|  | 
 | ||||||
|  |         m_gc_threshold *= 110; | ||||||
|  |         m_gc_threshold /= 100; | ||||||
|  |         m_gc_threshold++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::propagate() { | ||||||
|  |         SASSERT(s.s().at_base_lvl()); | ||||||
|  |         auto* n = m_queue; | ||||||
|  |         vv* k = nullptr; | ||||||
|  |         unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.get_config().m_dack_factor); | ||||||
|  |         num_prop = std::min(num_prop, m_table.size()); | ||||||
|  |         for (unsigned i = 0; i < num_prop; ++i, n = k) { | ||||||
|  |             k = n->next(); | ||||||
|  |             if (n->m_count < m_propagate_low_watermark && n->m_glue != 0) | ||||||
|  |                 continue;             | ||||||
|  |             add_cc(n->v1, n->v2); | ||||||
|  |             remove(n); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void ackerman::add_cc(euf::theory_var v1, euf::theory_var v2) { | ||||||
|  |         SASSERT(v1 < v2); | ||||||
|  |         if (static_cast<unsigned>(v2) >= s.get_num_vars()) | ||||||
|  |             return; | ||||||
|  |         if (!s.var2enode(v1) || !s.var2enode(v2)) | ||||||
|  |             return; | ||||||
|  |         sort* s1 = s.m.get_sort(s.var2expr(v1)); | ||||||
|  |         sort* s2 = s.m.get_sort(s.var2expr(v2)); | ||||||
|  |         if (s1 != s2 || !s.bv.is_bv_sort(s1)) | ||||||
|  |             return; | ||||||
|  |         IF_VERBOSE(0, verbose_stream() << "assert ackerman " << v1 << " " << v2 << "\n"); | ||||||
|  |         s.assert_ackerman(v1, v2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								src/sat/smt/bv_ackerman.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/sat/smt/bv_ackerman.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2020 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     euf_ackerman.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Ackerman reduction plugin for EUF | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2020-08-25 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "util/dlist.h" | ||||||
|  | #include "sat/smt/atom2bool_var.h" | ||||||
|  | #include "sat/smt/sat_th.h" | ||||||
|  | 
 | ||||||
|  | namespace bv { | ||||||
|  | 
 | ||||||
|  |     class solver; | ||||||
|  | 
 | ||||||
|  |     class ackerman { | ||||||
|  | 
 | ||||||
|  |         struct vv : dll_base<vv> { | ||||||
|  |             euf::theory_var v1, v2; | ||||||
|  |             unsigned   m_count{ 0 }; | ||||||
|  |             unsigned   m_glue{ UINT_MAX }; | ||||||
|  |             vv():v1(euf::null_theory_var), v2(euf::null_theory_var) {} | ||||||
|  |             vv(euf::theory_var v1, euf::theory_var v2):v1(v1), v2(v2) {} | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct vv_eq { | ||||||
|  |             bool operator()(vv const* a, vv const* b) const { | ||||||
|  |                 return a->v1 == b->v1 && a->v2 == b->v2; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |          | ||||||
|  |         struct vv_hash { | ||||||
|  |             unsigned operator()(vv const* a) const { | ||||||
|  |                 return mk_mix(a->v1, a->v2, 0); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         typedef hashtable<vv*, vv_hash, vv_eq> table_t; | ||||||
|  | 
 | ||||||
|  |         solver&       s; | ||||||
|  |         table_t       m_table; | ||||||
|  |         vv*           m_queue { nullptr }; | ||||||
|  |         vv*           m_tmp_vv { nullptr }; | ||||||
|  |         unsigned      m_gc_threshold { 1 }; | ||||||
|  |         unsigned      m_propagate_high_watermark { 10000 }; | ||||||
|  |         unsigned      m_propagate_low_watermark { 10 }; | ||||||
|  |         unsigned      m_num_propagations_since_last_gc { 0 }; | ||||||
|  |         bool_vector   m_diff_levels; | ||||||
|  | 
 | ||||||
|  |         void update_glue(vv& v); | ||||||
|  |         void reset(); | ||||||
|  |         void new_tmp(); | ||||||
|  |         void remove(vv* inf); | ||||||
|  |         void gc(); | ||||||
|  |         void add_cc(euf::theory_var v1, euf::theory_var v2); | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         ackerman(solver& s); | ||||||
|  |         ~ackerman(); | ||||||
|  | 
 | ||||||
|  |         void used_eq_eh(euf::theory_var v1, euf::theory_var v2); | ||||||
|  | 
 | ||||||
|  |         void propagate(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | @ -22,7 +22,17 @@ Author: | ||||||
| 
 | 
 | ||||||
| namespace bv { | namespace bv { | ||||||
| 
 | 
 | ||||||
|     class add_var_pos_trail : public trail<euf::solver> { |     solver::bit_atom& solver::atom::to_bit() { | ||||||
|  |         SASSERT(is_bit()); | ||||||
|  |         return dynamic_cast<bit_atom&>(*this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     solver::def_atom& solver::atom::to_def() { | ||||||
|  |         SASSERT(!is_bit()); | ||||||
|  |         return dynamic_cast<def_atom&>(*this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     class solver::add_var_pos_trail : public trail<euf::solver> { | ||||||
|         solver::bit_atom* m_atom; |         solver::bit_atom* m_atom; | ||||||
|     public: |     public: | ||||||
|         add_var_pos_trail(solver::bit_atom* a) :m_atom(a) {} |         add_var_pos_trail(solver::bit_atom* a) :m_atom(a) {} | ||||||
|  | @ -32,11 +42,11 @@ namespace bv { | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     class mk_atom_trail : public trail<euf::solver> { |     class solver::mk_atom_trail : public trail<euf::solver> { | ||||||
|         solver& th; |         solver& th; | ||||||
|         sat::bool_var m_var; |         sat::bool_var m_var; | ||||||
|     public: |     public: | ||||||
|         mk_atom_trail(sat::bool_var v, solver& th):m_var(v), th(th) {} |         mk_atom_trail(sat::bool_var v, solver& th) : th(th), m_var(v) {} | ||||||
|         void undo(euf::solver& euf) override { |         void undo(euf::solver& euf) override { | ||||||
|             solver::atom* a = th.get_bv2a(m_var); |             solver::atom* a = th.get_bv2a(m_var); | ||||||
|             a->~atom(); |             a->~atom(); | ||||||
|  | @ -51,160 +61,150 @@ namespace bv { | ||||||
|         m_wpos.push_back(0); |         m_wpos.push_back(0); | ||||||
|         m_zero_one_bits.push_back(zero_one_bits());        |         m_zero_one_bits.push_back(zero_one_bits());        | ||||||
|         ctx.attach_th_var(n, this, r); |         ctx.attach_th_var(n, this, r); | ||||||
|  |          | ||||||
|  |         TRACE("bv", tout << "mk-var: " << r << " " << n->get_owner_id() << " " << mk_pp(n->get_owner(), m) << "\n";); | ||||||
|         return r; |         return r; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void solver::apply_sort_cnstr(euf::enode * n, sort * s) { | ||||||
|  |         get_var(n); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { |     sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { | ||||||
|  |         SASSERT(m.is_bool(e)); | ||||||
|         if (!visit_rec(m, e, sign, root, redundant)) |         if (!visit_rec(m, e, sign, root, redundant)) | ||||||
|             return sat::null_literal; |             return sat::null_literal; | ||||||
|         return sat::null_literal; |         return expr2literal(e); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize(expr* e, bool redundant) { | ||||||
|  |         visit_rec(m, e, false, false, redundant); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::visit(expr* e) {       |     bool solver::visit(expr* e) {       | ||||||
|         if (!bv.is_bv(e)) { |         if (!is_app(e) || to_app(e)->get_family_id() != get_id()) { | ||||||
|             ctx.internalize(e, false, false, m_is_redundant); |             ctx.internalize(e, m_is_redundant); | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         m_args.reset(); |         m_stack.push_back(sat::eframe(e)); | ||||||
|         app* a = to_app(e); |  | ||||||
|         for (expr* arg : *a) { |  | ||||||
|             euf::enode* n = get_enode(arg); |  | ||||||
|             if (n) |  | ||||||
|                 m_args.push_back(n); |  | ||||||
|             else |  | ||||||
|                 m_stack.push_back(sat::eframe(arg)); |  | ||||||
|         } |  | ||||||
|         if (m_args.size() != a->get_num_args()) |  | ||||||
|         return false; |         return false; | ||||||
|         if (!smt_params().m_bv_reflect) |  | ||||||
|             m_args.reset(); |  | ||||||
|         euf::enode* n = mk_enode(a, m_args); |  | ||||||
|         SASSERT(n->interpreted()); |  | ||||||
|         theory_var v = n->get_th_var(get_id()); |  | ||||||
| 
 |  | ||||||
|         switch (a->get_decl_kind()) { |  | ||||||
|         case OP_BV_NUM:         internalize_num(a, v); break; |  | ||||||
|         default:                break; |  | ||||||
|         } |  | ||||||
|         return true; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::visited(expr* e) { |     bool solver::visited(expr* e) { | ||||||
|         return get_enode(e) != nullptr; |         euf::enode* n = expr2enode(e); | ||||||
|  |         return n && n->is_attached_to(get_id());         | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool solver::post_visit(expr* e, bool sign, bool root) { | ||||||
|  |         euf::enode* n = expr2enode(e); | ||||||
|  |         app* a = to_app(e); | ||||||
|  |          | ||||||
|  |         SASSERT(!n || !n->is_attached_to(get_id())); | ||||||
|  |         if (!n) { | ||||||
|  |             m_args.reset();             | ||||||
|  |             if (get_config().m_bv_reflect || m.is_considered_uninterpreted(a->get_decl())) { | ||||||
|  |                 for (expr* arg : *a) | ||||||
|  |                     m_args.push_back(expr2enode(arg)); | ||||||
|  |             } | ||||||
|  |             DEBUG_CODE(for (auto* n : m_args) VERIFY(n);); | ||||||
|  |             n = ctx.mk_enode(e, m_args.size(), m_args.c_ptr()); | ||||||
|  |             SASSERT(!n->is_attached_to(get_id())); | ||||||
|  |             ctx.attach_node(n); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         SASSERT(!n->is_attached_to(get_id())); | ||||||
|  |         theory_var v = mk_var(n); | ||||||
|  |         SASSERT(n->is_attached_to(get_id())); | ||||||
|  | 
 | ||||||
|  |         std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits)> bin; | ||||||
|  |         std::function<void(unsigned sz, expr* const* xs, expr* const* ys, expr_ref& bit)> ebin; | ||||||
|  |         std::function<void(unsigned sz, expr* const* xs, expr_ref_vector& bits)> un; | ||||||
|  |         std::function<void(unsigned sz, expr* const* xs, unsigned p, expr_ref_vector& bits)> pun; | ||||||
|  | #define internalize_bin(F) bin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits) { m_bb.F(sz, xs, ys, bits); }; internalize_binary(a, bin); | ||||||
|  | #define internalize_un(F) un = [&](unsigned sz, expr* const* xs, expr_ref_vector& bits) { m_bb.F(sz, xs, bits);}; internalize_unary(a, un); | ||||||
|  | #define internalize_ac(F) bin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref_vector& bits) { m_bb.F(sz, xs, ys, bits); }; internalize_ac_binary(a, bin); | ||||||
|  | #define internalize_pun(F) pun = [&](unsigned sz, expr* const* xs, unsigned p, expr_ref_vector& bits) { m_bb.F(sz, xs, p, bits);}; internalize_par_unary(a, pun); | ||||||
|  | #define internalize_nfl(F) ebin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref& out) { m_bb.F(sz, xs, ys, out);}; internalize_novfl(a, ebin); | ||||||
|  | 
 | ||||||
|  |         switch (a->get_decl_kind()) { | ||||||
|  |         case OP_BV_NUM:           internalize_num(a, v); break; | ||||||
|  |         case OP_BNOT:             internalize_un(mk_not); break; | ||||||
|  |         case OP_BREDAND:          internalize_un(mk_redand); break; | ||||||
|  |         case OP_BREDOR:           internalize_un(mk_redor); break; | ||||||
|  |         case OP_BSDIV_I:          internalize_bin(mk_sdiv); break; | ||||||
|  |         case OP_BUDIV_I:          internalize_bin(mk_udiv); break; | ||||||
|  |         case OP_BUREM_I:          internalize_bin(mk_urem); break; | ||||||
|  |         case OP_BSREM_I:          internalize_bin(mk_srem); break; | ||||||
|  |         case OP_BSMOD_I:          internalize_bin(mk_smod); break; | ||||||
|  |         case OP_BSHL:             internalize_bin(mk_shl); break; | ||||||
|  |         case OP_BLSHR:            internalize_bin(mk_lshr); break; | ||||||
|  |         case OP_BASHR:            internalize_bin(mk_ashr); break; | ||||||
|  |         case OP_EXT_ROTATE_LEFT:  internalize_bin(mk_ext_rotate_left); break; | ||||||
|  |         case OP_EXT_ROTATE_RIGHT: internalize_bin(mk_ext_rotate_right); break; | ||||||
|  |         case OP_BADD:             internalize_ac(mk_adder); break; | ||||||
|  |         case OP_BMUL:             internalize_ac(mk_multiplier); break; | ||||||
|  |         case OP_BAND:             internalize_ac(mk_and); break; | ||||||
|  |         case OP_BOR:              internalize_ac(mk_or); break; | ||||||
|  |         case OP_BXOR:             internalize_ac(mk_xor); break; | ||||||
|  |         case OP_BNAND:            internalize_bin(mk_nand); break; | ||||||
|  |         case OP_BNOR:             internalize_bin(mk_nor); break; | ||||||
|  |         case OP_BXNOR:            internalize_bin(mk_xnor); break; | ||||||
|  |         case OP_BCOMP:            internalize_bin(mk_comp); break; | ||||||
|  |         case OP_SIGN_EXT:         internalize_pun(mk_sign_extend); break; | ||||||
|  |         case OP_ZERO_EXT:         internalize_pun(mk_zero_extend); break; | ||||||
|  |         case OP_ROTATE_LEFT:      internalize_pun(mk_rotate_left); break; | ||||||
|  |         case OP_ROTATE_RIGHT:     internalize_pun(mk_rotate_right); break; | ||||||
|  |         case OP_BUMUL_NO_OVFL:    internalize_nfl(mk_umul_no_overflow); break; | ||||||
|  |         case OP_BSMUL_NO_OVFL:    internalize_nfl(mk_smul_no_overflow); break; | ||||||
|  |         case OP_BSMUL_NO_UDFL:    internalize_nfl(mk_smul_no_underflow); break; | ||||||
|  |         case OP_BIT2BOOL:         internalize_bit2bool(a); break; | ||||||
|  |         case OP_ULEQ:             internalize_le<false>(a); break; | ||||||
|  |         case OP_SLEQ:             internalize_le<true>(a); break; | ||||||
|  |         case OP_XOR3:             internalize_xor3(a); break; | ||||||
|  |         case OP_CARRY:            internalize_carry(a); break; | ||||||
|  |         case OP_BSUB:             internalize_sub(a); break; | ||||||
|  |         case OP_CONCAT:           internalize_concat(a); break; | ||||||
|  |         case OP_EXTRACT:          internalize_extract(a); break; | ||||||
|  |         case OP_MKBV:             internalize_mkbv(a); break; | ||||||
|  |         case OP_INT2BV:           internalize_int2bv(a); break; | ||||||
|  |         case OP_BV2INT:           internalize_bv2int(a); break; | ||||||
|  |         case OP_BSDIV0:           break; | ||||||
|  |         case OP_BUDIV0:           break; | ||||||
|  |         case OP_BSREM0:           break; | ||||||
|  |         case OP_BUREM0:           break; | ||||||
|  |         case OP_BSMOD0:           break; | ||||||
|  |         default: | ||||||
|  |             UNREACHABLE(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::mk_bits(theory_var v) { |     void solver::mk_bits(theory_var v) { | ||||||
|         TRACE("bv", tout << "v" << v << "\n";); |         TRACE("bv", tout << "v" << v << "\n";); | ||||||
|         expr* e = get_expr(v); |         expr* e = var2expr(v); | ||||||
|         unsigned bv_size = get_bv_size(v); |         unsigned bv_size = get_bv_size(v); | ||||||
|         literal_vector& bits = m_bits[v]; |         m_bits[v].reset(); | ||||||
|         bits.reset(); |  | ||||||
|         for (unsigned i = 0; i < bv_size; i++) { |         for (unsigned i = 0; i < bv_size; i++) { | ||||||
|             expr_ref b2b = mk_bit2bool(e, i); |             expr_ref b2b(bv.mk_bit2bool(e, i), m); | ||||||
|             bits.push_back(ctx.internalize(b2b, false, false, m_is_redundant)); |             sat::literal lit = ctx.internalize(b2b, false, false, m_is_redundant); | ||||||
|  |             m_bits[v].push_back(lit); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     expr_ref solver::mk_bit2bool(expr* e, unsigned idx) { |  | ||||||
|         parameter p(idx); |  | ||||||
|         expr* args[1] = { e }; |  | ||||||
|         return expr_ref(m.mk_app(get_id(), OP_BIT2BOOL, 1, &p, 1, args), m); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| #if 0 |  | ||||||
|         SASSERT(!ctx.b_internalized(n)); |  | ||||||
| 
 |  | ||||||
|         TRACE("bv", tout << "bit2bool: " << mk_pp(n, ctx.get_manager()) << "\n";); |  | ||||||
|         expr* first_arg = n->get_arg(0); |  | ||||||
| 
 |  | ||||||
|         if (!ctx.e_internalized(first_arg)) { |  | ||||||
|             // This may happen if bit2bool(x) is in a conflict
 |  | ||||||
|             // clause that is being reinitialized, and x was not reinitialized
 |  | ||||||
|             // yet.
 |  | ||||||
|             // So, we internalize x (i.e., arg)
 |  | ||||||
|             ctx.internalize(first_arg, false); |  | ||||||
|             SASSERT(ctx.e_internalized(first_arg)); |  | ||||||
|             // In most cases, when x is internalized, its bits are created.
 |  | ||||||
|             // They are created because x is a bit-vector operation or apply_sort_cnstr is invoked.
 |  | ||||||
|             // However, there is an exception. The method apply_sort_cnstr is not invoked for ite-terms.
 |  | ||||||
|             // So, I execute get_var on the enode attached to first_arg. 
 |  | ||||||
|             // This will force a theory variable to be created if it does not already exist.
 |  | ||||||
|             // This will also force the creation of all bits for x.
 |  | ||||||
|             enode* first_arg_enode = ctx.get_enode(first_arg); |  | ||||||
|             get_var(first_arg_enode); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         enode* arg = ctx.get_enode(first_arg); |  | ||||||
|         // The argument was already internalized, but it may not have a theory variable associated with it.
 |  | ||||||
|         // For example, for ite-terms the method apply_sort_cnstr is not invoked.
 |  | ||||||
|         // See comment in the then-branch.
 |  | ||||||
|         theory_var v_arg = arg->get_th_var(get_id()); |  | ||||||
|         if (v_arg == null_theory_var) { |  | ||||||
|             // The method get_var will create a theory variable for arg. 
 |  | ||||||
|             // As a side-effect the bits for arg will also be created.
 |  | ||||||
|             get_var(arg); |  | ||||||
|             SASSERT(ctx.b_internalized(n)); |  | ||||||
|         } |  | ||||||
|         else if (!ctx.b_internalized(n)) { |  | ||||||
|             SASSERT(v_arg != null_theory_var); |  | ||||||
|             bool_var bv = ctx.mk_bool_var(n); |  | ||||||
|             ctx.set_var_theory(bv, get_id()); |  | ||||||
|             bit_atom* a = new (get_region()) bit_atom(); |  | ||||||
|             insert_bv2a(bv, a); |  | ||||||
|             m_trail_stack.push(mk_atom_trail(bv)); |  | ||||||
|             unsigned idx = n->get_decl()->get_parameter(0).get_int(); |  | ||||||
|             SASSERT(a->m_occs == 0); |  | ||||||
|             a->m_occs = new (get_region()) var_pos_occ(v_arg, idx); |  | ||||||
|             // Fix for #2182, and SPACER bit-vector
 |  | ||||||
|             if (idx < m_bits[v_arg].size()) { |  | ||||||
|                 ctx.mk_th_axiom(get_id(), m_bits[v_arg][idx], literal(bv, true)); |  | ||||||
|                 ctx.mk_th_axiom(get_id(), ~m_bits[v_arg][idx], literal(bv, false)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         // axiomatize bit2bool on constants.
 |  | ||||||
|         rational val; |  | ||||||
|         unsigned sz; |  | ||||||
|         if (m_util.is_numeral(first_arg, val, sz)) { |  | ||||||
|             rational bit; |  | ||||||
|             unsigned idx = n->get_decl()->get_parameter(0).get_int(); |  | ||||||
|             div(val, rational::power_of_two(idx), bit); |  | ||||||
|             mod(bit, rational(2), bit); |  | ||||||
|             literal lit = ctx.get_literal(n); |  | ||||||
|             if (bit.is_zero()) lit.neg(); |  | ||||||
|             ctx.mark_as_relevant(lit); |  | ||||||
|             ctx.mk_th_axiom(get_id(), 1, &lit); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     euf::enode * solver::mk_enode(expr * n, ptr_vector<euf::enode> const& args) { |  | ||||||
|         euf::enode * e = ctx.get_enode(n); |  | ||||||
|         if (!e) { |  | ||||||
|             e = ctx.mk_enode(n, args.size(), args.c_ptr()); |  | ||||||
|             mk_var(e); |  | ||||||
|         } |  | ||||||
|         return e; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     euf::theory_var solver::get_var(euf::enode* n) { |     euf::theory_var solver::get_var(euf::enode* n) { | ||||||
|         theory_var v = n->get_th_var(get_id()); |         theory_var v = n->get_th_var(get_id()); | ||||||
|         if (v == euf::null_theory_var) { |         if (v == euf::null_theory_var) { | ||||||
|             v = mk_var(n); |             v = mk_var(n); | ||||||
|  |             if (bv.is_bv(n->get_owner()))  | ||||||
|                 mk_bits(v);             |                 mk_bits(v);             | ||||||
|         } |         } | ||||||
|         return v; |         return v; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     euf::enode* solver::get_arg(euf::enode* n, unsigned idx) { |     euf::enode* solver::get_arg(euf::enode* n, unsigned idx) { | ||||||
|         if (get_config().m_bv_reflect) { |         app* o = to_app(n->get_owner()); | ||||||
|             return n->get_arg(idx); |         return ctx.get_enode(o->get_arg(idx)); | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             expr* arg = n->get_arg(idx)->get_owner(); |  | ||||||
|             return ctx.get_enode(arg); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     inline euf::theory_var solver::get_arg_var(euf::enode* n, unsigned idx) { |     inline euf::theory_var solver::get_arg_var(euf::enode* n, unsigned idx) { | ||||||
|  | @ -212,20 +212,14 @@ namespace bv { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::get_bits(theory_var v, expr_ref_vector& r) { |     void solver::get_bits(theory_var v, expr_ref_vector& r) { | ||||||
|         literal_vector& bits = m_bits[v]; |         for (literal lit : m_bits[v])  | ||||||
|         for (literal lit : bits) { |             r.push_back(literal2expr(lit));         | ||||||
|             r.push_back(get_expr(lit)); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     inline void solver::get_bits(euf::enode* n, expr_ref_vector& r) { |     inline void solver::get_bits(euf::enode* n, expr_ref_vector& r) { | ||||||
|         get_bits(get_var(n), r); |         get_bits(get_var(n), r); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     inline void solver::get_arg_bits(euf::enode* n, unsigned idx, expr_ref_vector& r) { |  | ||||||
|         get_bits(get_arg_var(n, idx), r); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     inline void solver::get_arg_bits(app* n, unsigned idx, expr_ref_vector& r) { |     inline void solver::get_arg_bits(app* n, unsigned idx, expr_ref_vector& r) { | ||||||
|         app* arg = to_app(n->get_arg(idx)); |         app* arg = to_app(n->get_arg(idx)); | ||||||
|         get_bits(ctx.get_enode(arg), r); |         get_bits(ctx.get_enode(arg), r); | ||||||
|  | @ -242,18 +236,22 @@ namespace bv { | ||||||
|        \brief Add bit l to the given variable. |        \brief Add bit l to the given variable. | ||||||
|     */ |     */ | ||||||
|     void solver::add_bit(theory_var v, literal l) { |     void solver::add_bit(theory_var v, literal l) { | ||||||
|         literal_vector & bits = m_bits[v]; |         unsigned idx = m_bits[v].size(); | ||||||
|         unsigned idx          = bits.size(); |         m_bits[v].push_back(l); | ||||||
|         bits.push_back(l); |         s().set_external(l.var()); | ||||||
|         if (s().value(l) != l_undef && s().lvl(l) == 0) { |         set_bit_eh(v, l, idx); | ||||||
|             register_true_false_bit(v, idx); |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     void solver::set_bit_eh(theory_var v, literal l, unsigned idx) { | ||||||
|  |         if (s().value(l) != l_undef && s().lvl(l) == 0)  | ||||||
|  |             register_true_false_bit(v, idx); | ||||||
|         else { |         else { | ||||||
|             atom* a = get_bv2a(l.var()); |             atom* a = get_bv2a(l.var()); | ||||||
|             SASSERT(!a || a->is_bit()); |             if (a && !a->is_bit())  | ||||||
|             if (a) { |                 ; | ||||||
|                 bit_atom* b = static_cast<bit_atom*>(a); |             else if (a) { | ||||||
|                 find_new_diseq_axioms(b->m_occs, v, idx); |                 bit_atom* b = &a->to_bit(); | ||||||
|  |                 find_new_diseq_axioms(*b, v, idx); | ||||||
|                 ctx.push(add_var_pos_trail(b)); |                 ctx.push(add_var_pos_trail(b)); | ||||||
|                 b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs); |                 b->m_occs = new (get_region()) var_pos_occ(v, idx, b->m_occs); | ||||||
|             } |             } | ||||||
|  | @ -261,24 +259,23 @@ namespace bv { | ||||||
|                 bit_atom* b = new (get_region()) bit_atom(); |                 bit_atom* b = new (get_region()) bit_atom(); | ||||||
|                 insert_bv2a(l.var(), b); |                 insert_bv2a(l.var(), b); | ||||||
|                 ctx.push(mk_atom_trail(l.var(), *this)); |                 ctx.push(mk_atom_trail(l.var(), *this)); | ||||||
|                 SASSERT(b->m_occs == 0); |                 SASSERT(!b->m_occs); | ||||||
|                 b->m_occs = new (get_region()) var_pos_occ(v, idx); |                 b->m_occs = new (get_region()) var_pos_occ(v, idx); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::add_unit(sat::literal lit) { |     void solver::init_bits(expr* e, expr_ref_vector const& bits) { | ||||||
|         s().add_clause(1, &lit, status()); |         euf::enode* n = expr2enode(e); | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void solver::init_bits(euf::enode * n, expr_ref_vector const & bits) { |  | ||||||
|         SASSERT(get_bv_size(n) == bits.size()); |         SASSERT(get_bv_size(n) == bits.size()); | ||||||
|         SASSERT(euf::null_theory_var != n->get_th_var(get_id())); |         SASSERT(euf::null_theory_var != n->get_th_var(get_id())); | ||||||
|         theory_var v = n->get_th_var(get_id()); |         theory_var v = n->get_th_var(get_id()); | ||||||
|         unsigned sz = bits.size(); |  | ||||||
|         m_bits[v].reset(); |         m_bits[v].reset(); | ||||||
|         for (expr* bit : bits)  |         for (expr* bit : bits)  | ||||||
|             add_bit(v, get_literal(bit)); |             add_bit(v, ctx.internalize(bit, false, false, m_is_redundant));         | ||||||
|  |         for (expr* bit : bits) | ||||||
|  |             get_var(expr2enode(bit)); | ||||||
|  |         SASSERT(get_bv_size(n) == bits.size()); | ||||||
|         find_wpos(v); |         find_wpos(v); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -287,22 +284,23 @@ namespace bv { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     unsigned solver::get_bv_size(theory_var v) { |     unsigned solver::get_bv_size(theory_var v) { | ||||||
|         return get_bv_size(get_enode(v)); |         return get_bv_size(var2enode(v)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::internalize_num(app* n, theory_var v) { |     void solver::internalize_num(app* n, theory_var v) { | ||||||
|         numeral val; |         numeral val; | ||||||
|         unsigned sz = 0; |         unsigned sz = 0; | ||||||
|  |         SASSERT(expr2enode(n)->interpreted()); | ||||||
|         VERIFY(bv.is_numeral(n, val, sz)); |         VERIFY(bv.is_numeral(n, val, sz)); | ||||||
|         expr_ref_vector bits(m); |         expr_ref_vector bits(m); | ||||||
|         m_bb.num2bits(val, sz, bits); |         m_bb.num2bits(val, sz, bits); | ||||||
|         literal_vector & c_bits = m_bits[v]; |  | ||||||
|         SASSERT(bits.size() == sz); |         SASSERT(bits.size() == sz); | ||||||
|         SASSERT(c_bits.empty()); |         SASSERT(m_bits[v].empty()); | ||||||
|  |         sat::literal true_literal = ctx.internalize(m.mk_true(), false, false, false); | ||||||
|         for (unsigned i = 0; i < sz; i++) { |         for (unsigned i = 0; i < sz; i++) { | ||||||
|             expr* l = bits.get(i); |             expr* l = bits.get(i); | ||||||
|             SASSERT(m.is_true(l) || m.is_false(l)); |             SASSERT(m.is_true(l) || m.is_false(l)); | ||||||
|             c_bits.push_back(m.is_true(l) ? true_literal : false_literal); |             m_bits[v].push_back(m.is_true(l) ? true_literal : ~true_literal); | ||||||
|             register_true_false_bit(v, i); |             register_true_false_bit(v, i); | ||||||
|         } |         } | ||||||
|         fixed_var_eh(v); |         fixed_var_eh(v); | ||||||
|  | @ -310,13 +308,11 @@ namespace bv { | ||||||
| 
 | 
 | ||||||
|     void solver::internalize_mkbv(app* n) { |     void solver::internalize_mkbv(app* n) { | ||||||
|         expr_ref_vector bits(m); |         expr_ref_vector bits(m); | ||||||
|         euf::enode * e = mk_enode(n, m_args); |  | ||||||
|         bits.append(n->get_num_args(), n->get_args()); |         bits.append(n->get_num_args(), n->get_args()); | ||||||
|         init_bits(e, bits); |         init_bits(n, bits); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::internalize_bv2int(app* n) { |     void solver::internalize_bv2int(app* n) { | ||||||
|         mk_enode(n, m_args); |  | ||||||
|         assert_bv2int_axiom(n); |         assert_bv2int_axiom(n); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -327,33 +323,27 @@ namespace bv { | ||||||
| 
 | 
 | ||||||
|     void solver::assert_bv2int_axiom(app* n) { |     void solver::assert_bv2int_axiom(app* n) { | ||||||
|         expr* k = nullptr;         |         expr* k = nullptr;         | ||||||
|         sort * int_sort = m.get_sort(n); |         VERIFY(bv.is_bv2int(n, k)); | ||||||
|         SASSERT(bv.is_bv2int(n, k)); |  | ||||||
|         SASSERT(bv.is_bv_sort(m.get_sort(k))); |         SASSERT(bv.is_bv_sort(m.get_sort(k))); | ||||||
|         expr_ref_vector k_bits(m); |         expr_ref_vector k_bits(m); | ||||||
|         euf::enode * k_enode = mk_enode(k, m_args); |         euf::enode* k_enode = expr2enode(k); | ||||||
|         get_bits(k_enode, k_bits); |         get_bits(k_enode, k_bits); | ||||||
|         unsigned sz = bv.get_bv_size(k); |         unsigned sz = bv.get_bv_size(k); | ||||||
|         expr_ref_vector args(m); |         expr_ref_vector args(m); | ||||||
|         expr_ref zero(bv.mk_numeral(numeral(0), int_sort), m); |         expr_ref zero(m_autil.mk_int(0), m); | ||||||
|         numeral num(1); |         unsigned i = 0;         | ||||||
|         for (expr* b : k_bits) { |         for (expr* b : k_bits)  | ||||||
|             expr_ref n(m_autil.mk_numeral(num, int_sort), m); |             args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));         | ||||||
|             args.push_back(m.mk_ite(b, n, zero)); |  | ||||||
|             num *= numeral(2); |  | ||||||
|         } |  | ||||||
|         expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m); |         expr_ref sum(m_autil.mk_add(sz, args.c_ptr()), m); | ||||||
|         expr_ref eq(m.mk_eq(n, sum), m); |         expr_ref eq(m.mk_eq(n, sum), m); | ||||||
|         sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant); |         sat::literal lit = ctx.internalize(eq, false, false, m_is_redundant); | ||||||
|         add_unit(lit); |         add_unit(lit); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     void solver::internalize_int2bv(app* n) { |     void solver::internalize_int2bv(app* n) { | ||||||
|         SASSERT(bv.is_int2bv(n)); |         SASSERT(bv.is_int2bv(n)); | ||||||
|         euf::enode* e = mk_enode(n, m_args); |         euf::enode* e = expr2enode(n); | ||||||
|         theory_var v = e->get_th_var(get_id()); |         mk_bits(e->get_th_var(get_id())); | ||||||
|         mk_bits(v); |  | ||||||
|         assert_int2bv_axiom(n); |         assert_int2bv_axiom(n); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -367,130 +357,234 @@ namespace bv { | ||||||
|      * for i = 0,.., sz-1 |      * for i = 0,.., sz-1 | ||||||
|      */ |      */ | ||||||
|     void solver::assert_int2bv_axiom(app* n) { |     void solver::assert_int2bv_axiom(app* n) { | ||||||
|         SASSERT(bv.is_int2bv(n)); |         expr* e = nullptr; | ||||||
|         expr* e = n->get_arg(0); |         VERIFY(bv.is_int2bv(n, e));       | ||||||
|         euf::enode* n_enode = mk_enode(n, m_args); |         euf::enode* n_enode = expr2enode(n); | ||||||
|         parameter param(m_autil.mk_int()); |  | ||||||
|         expr* n_expr = n; |  | ||||||
|         expr_ref lhs(m), rhs(m); |         expr_ref lhs(m), rhs(m); | ||||||
|         lhs = m.mk_app(get_id(), OP_BV2INT, 1, ¶m, 1, &n_expr); |         lhs = bv.mk_bv2int(n); | ||||||
|         unsigned sz = bv.get_bv_size(n); |         unsigned sz = bv.get_bv_size(n); | ||||||
|         numeral mod = power(numeral(2), sz); |         numeral mod = power(numeral(2), sz); | ||||||
|         rhs = m_autil.mk_mod(e, m_autil.mk_numeral(mod, true)); |         rhs = m_autil.mk_mod(e, m_autil.mk_int(mod)); | ||||||
|         expr_ref eq(m.mk_eq(lhs, rhs), m); |         expr_ref eq(m.mk_eq(lhs, rhs), m); | ||||||
|         literal l = ctx.internalize(eq, false, false, m_is_redundant); |  | ||||||
|         add_unit(l); |  | ||||||
| 
 |  | ||||||
|         TRACE("bv", tout << eq << "\n";); |         TRACE("bv", tout << eq << "\n";); | ||||||
|  |         add_unit(ctx.internalize(eq, false, false, m_is_redundant)); | ||||||
|         |         | ||||||
|         expr_ref_vector n_bits(m); |         expr_ref_vector n_bits(m); | ||||||
|          |  | ||||||
|         get_bits(n_enode, n_bits); |         get_bits(n_enode, n_bits); | ||||||
| 
 | 
 | ||||||
|         for (unsigned i = 0; i < sz; ++i) { |         for (unsigned i = 0; i < sz; ++i) { | ||||||
|             numeral div = power(numeral(2), i); |             numeral div = power2(i); | ||||||
|             mod = numeral(2); |             rhs = m_autil.mk_idiv(e, m_autil.mk_int(div)); | ||||||
|             rhs = m_autil.mk_idiv(e, m_autil.mk_numeral(div, true)); |             rhs = m_autil.mk_mod(rhs, m_autil.mk_int(2)); | ||||||
|             rhs = m_autil.mk_mod(rhs, m_autil.mk_numeral(mod, true)); |             rhs = m.mk_eq(rhs, m_autil.mk_int(1)); | ||||||
|             rhs = m.mk_eq(rhs, m_autil.mk_numeral(rational(1), true)); |  | ||||||
|             lhs = n_bits.get(i); |             lhs = n_bits.get(i); | ||||||
|             expr_ref eq(m.mk_eq(lhs, rhs), m); |             expr_ref eq(m.mk_eq(lhs, rhs), m); | ||||||
|             TRACE("bv", tout << eq << "\n";); |             TRACE("bv", tout << eq << "\n";); | ||||||
|             l = ctx.internalize(eq, false, false, m_is_redundant); |             add_unit(ctx.internalize(eq, false, false, m_is_redundant)); | ||||||
|             add_unit(l); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |     template<bool Signed> | ||||||
|     void solver::internalize_add(app* n) {} |     void solver::internalize_le(app* n) { | ||||||
|     void solver::internalize_sub(app* n) {} |         SASSERT(n->get_num_args() == 2); | ||||||
|     void solver::internalize_mul(app* n) {} |         expr_ref_vector arg1_bits(m), arg2_bits(m); | ||||||
|     void solver::internalize_udiv(app* n) {} |         get_arg_bits(n, 0, arg1_bits); | ||||||
|     void solver::internalize_sdiv(app* n) {} |         get_arg_bits(n, 1, arg2_bits); | ||||||
|     void solver::internalize_urem(app* n) {} |         expr_ref le(m); | ||||||
|     void solver::internalize_srem(app* n) {} |         if (Signed) | ||||||
|     void solver::internalize_smod(app* n) {} |             m_bb.mk_sle(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le); | ||||||
|     void solver::internalize_shl(app* n) {} |         else | ||||||
|     void solver::internalize_lshr(app* n) {} |             m_bb.mk_ule(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), le); | ||||||
|     void solver::internalize_ashr(app* n) {} |         literal def = ctx.internalize(le, false, false, m_is_redundant); | ||||||
|     void solver::internalize_ext_rotate_left(app* n) {} |         add_def(def, expr2literal(n)); | ||||||
|     void solver::internalize_ext_rotate_right(app* n) {} |  | ||||||
|     void solver::internalize_and(app* n) {} |  | ||||||
|     void solver::internalize_or(app* n) {} |  | ||||||
|     void solver::internalize_not(app* n) {} |  | ||||||
|     void solver::internalize_nand(app* n) {} |  | ||||||
|     void solver::internalize_nor(app* n) {} |  | ||||||
|     void solver::internalize_xor(app* n) {} |  | ||||||
|     void solver::internalize_xnor(app* n) {} |  | ||||||
|     void solver::internalize_concat(app* n) {} |  | ||||||
|     void solver::internalize_sign_extend(app* n) {} |  | ||||||
|     void solver::internalize_zero_extend(app* n) {} |  | ||||||
|     void solver::internalize_extract(app* n) {} |  | ||||||
|     void solver::internalize_redand(app* n) {} |  | ||||||
|     void solver::internalize_redor(app* n) {} |  | ||||||
|     void solver::internalize_comp(app* n) {} |  | ||||||
|     void solver::internalize_rotate_left(app* n) {} |  | ||||||
|     void solver::internalize_rotate_right(app* n) {} |  | ||||||
|     void solver::internalize_umul_no_overflow(app* n) {} |  | ||||||
|     void solver::internalize_smul_no_overflow(app* n) {} |  | ||||||
|     void solver::internalize_smul_no_underflow(app* n) {} |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |     void solver::internalize_carry(app* n) { | ||||||
| 
 |         SASSERT(n->get_num_args() == 3); | ||||||
| #if 0 |         literal r = ctx.get_literal(n); | ||||||
| 
 |         literal l1 = ctx.get_literal(n->get_arg(0)); | ||||||
|         case OP_BADD:           internalize_add(term); return true; |         literal l2 = ctx.get_literal(n->get_arg(1)); | ||||||
|         case OP_BSUB:           internalize_sub(term); return true; |         literal l3 = ctx.get_literal(n->get_arg(2)); | ||||||
|         case OP_BMUL:           internalize_mul(term); return true; |         add_clause(~r, l1, l2); | ||||||
|         case OP_BSDIV_I:        internalize_sdiv(term); return true; |         add_clause(~r, l1, l3); | ||||||
|         case OP_BUDIV_I:        internalize_udiv(term); return true; |         add_clause(~r, l2, l3); | ||||||
|         case OP_BSREM_I:        internalize_srem(term); return true; |         add_clause(r, ~l1, ~l2); | ||||||
|         case OP_BUREM_I:        internalize_urem(term); return true; |         add_clause(r, ~l1, ~l3); | ||||||
|         case OP_BSMOD_I:        internalize_smod(term); return true; |         add_clause(r, ~l2, ~l3); | ||||||
|         case OP_BAND:           internalize_and(term); return true; |  | ||||||
|         case OP_BOR:            internalize_or(term); return true; |  | ||||||
|         case OP_BNOT:           internalize_not(term); return true; |  | ||||||
|         case OP_BXOR:           internalize_xor(term); return true; |  | ||||||
|         case OP_BNAND:          internalize_nand(term); return true; |  | ||||||
|         case OP_BNOR:           internalize_nor(term); return true; |  | ||||||
|         case OP_BXNOR:          internalize_xnor(term); return true; |  | ||||||
|         case OP_CONCAT:         internalize_concat(term); return true; |  | ||||||
|         case OP_SIGN_EXT:       internalize_sign_extend(term); return true; |  | ||||||
|         case OP_ZERO_EXT:       internalize_zero_extend(term); return true; |  | ||||||
|         case OP_EXTRACT:        internalize_extract(term); return true; |  | ||||||
|         case OP_BREDOR:         internalize_redor(term); return true; |  | ||||||
|         case OP_BREDAND:        internalize_redand(term); return true; |  | ||||||
|         case OP_BCOMP:          internalize_comp(term); return true; |  | ||||||
|         case OP_BSHL:           internalize_shl(term); return true; |  | ||||||
|         case OP_BLSHR:          internalize_lshr(term); return true; |  | ||||||
|         case OP_BASHR:          internalize_ashr(term); return true; |  | ||||||
|         case OP_ROTATE_LEFT:    internalize_rotate_left(term); return true; |  | ||||||
|         case OP_ROTATE_RIGHT:   internalize_rotate_right(term); return true; |  | ||||||
|         case OP_EXT_ROTATE_LEFT:  internalize_ext_rotate_left(term); return true; |  | ||||||
|         case OP_EXT_ROTATE_RIGHT: internalize_ext_rotate_right(term); return true; |  | ||||||
|         case OP_BSDIV0:         return false; |  | ||||||
|         case OP_BUDIV0:         return false; |  | ||||||
|         case OP_BSREM0:         return false; |  | ||||||
|         case OP_BUREM0:         return false; |  | ||||||
|         case OP_BSMOD0:         return false; |  | ||||||
|         case OP_MKBV:           internalize_mkbv(term); return true; |  | ||||||
|         case OP_INT2BV: |  | ||||||
|             if (params().m_bv_enable_int2bv2int) { |  | ||||||
|                 internalize_int2bv(term); |  | ||||||
|             } |  | ||||||
|             return params().m_bv_enable_int2bv2int; |  | ||||||
|         case OP_BV2INT: |  | ||||||
|             if (params().m_bv_enable_int2bv2int) { |  | ||||||
|                 internalize_bv2int(term); |  | ||||||
|             } |  | ||||||
|             return params().m_bv_enable_int2bv2int; |  | ||||||
|         default: |  | ||||||
|             TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, m) << "\n";); |  | ||||||
|             UNREACHABLE(); |  | ||||||
|             return false; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void solver::internalize_xor3(app* n) { | ||||||
|  |         SASSERT(n->get_num_args() == 3); | ||||||
|  |         literal r = ctx.get_literal(n); | ||||||
|  |         literal l1 = expr2literal(n->get_arg(0)); | ||||||
|  |         literal l2 = expr2literal(n->get_arg(1)); | ||||||
|  |         literal l3 = expr2literal(n->get_arg(2)); | ||||||
|  |         add_clause(~r, l1, l2, l3); | ||||||
|  |         add_clause(~r, ~l1, ~l2, l3); | ||||||
|  |         add_clause(~r, ~l1, l2, ~l3); | ||||||
|  |         add_clause(~r, l1, ~l2, ~l3); | ||||||
|  |         add_clause(r, ~l1, l2, l3); | ||||||
|  |         add_clause(r, l1, ~l2, l3); | ||||||
|  |         add_clause(r, l1, l2, ~l3); | ||||||
|  |         add_clause(r, ~l1, ~l2, ~l3); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn) { | ||||||
|  |         SASSERT(n->get_num_args() == 1); | ||||||
|  |         expr_ref_vector arg1_bits(m), bits(m); | ||||||
|  |         get_arg_bits(n, 0, arg1_bits); | ||||||
|  |         fn(arg1_bits.size(), arg1_bits.c_ptr(), bits); | ||||||
|  |         init_bits(n, bits); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_par_unary(app* n, std::function<void(unsigned, expr* const*, unsigned p, expr_ref_vector&)>& fn) { | ||||||
|  |         SASSERT(n->get_num_args() == 1); | ||||||
|  |         expr_ref_vector arg1_bits(m), bits(m); | ||||||
|  |         get_arg_bits(n, 0, arg1_bits); | ||||||
|  |         unsigned param = n->get_decl()->get_parameter(0).get_int(); | ||||||
|  |         fn(arg1_bits.size(), arg1_bits.c_ptr(), param, bits); | ||||||
|  |         init_bits(n, bits); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn) { | ||||||
|  |         SASSERT(n->get_num_args() == 2); | ||||||
|  |         expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); | ||||||
|  |         get_arg_bits(n, 0, arg1_bits); | ||||||
|  |         get_arg_bits(n, 1, arg2_bits); | ||||||
|  |         SASSERT(arg1_bits.size() == arg2_bits.size()); | ||||||
|  |         fn(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits); | ||||||
|  |         init_bits(n, bits); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_ac_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn) { | ||||||
|  |         SASSERT(n->get_num_args() >= 2); | ||||||
|  |         expr_ref_vector bits(m), new_bits(m), arg_bits(m); | ||||||
|  |         unsigned i = n->get_num_args() - 1;         | ||||||
|  |         get_arg_bits(n, i, bits); | ||||||
|  |         for (; i-- > 0; ) { | ||||||
|  |             arg_bits.reset(); | ||||||
|  |             get_arg_bits(n, i, arg_bits); | ||||||
|  |             SASSERT(arg_bits.size() == bits.size()); | ||||||
|  |             new_bits.reset(); | ||||||
|  |             fn(arg_bits.size(), arg_bits.c_ptr(), bits.c_ptr(), new_bits); | ||||||
|  |             bits.swap(new_bits); | ||||||
|  |         } | ||||||
|  |         init_bits(n, bits); | ||||||
|  |         TRACE("bv_verbose", tout << arg_bits << " " << bits << " " << new_bits << "\n";); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn) { | ||||||
|  |         SASSERT(n->get_num_args() == 2); | ||||||
|  |         expr_ref_vector arg1_bits(m), arg2_bits(m); | ||||||
|  |         get_arg_bits(n, 0, arg1_bits); | ||||||
|  |         get_arg_bits(n, 1, arg2_bits); | ||||||
|  |         expr_ref out(m); | ||||||
|  |         fn(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), out); | ||||||
|  |         sat::literal def = ctx.internalize(out, false, false, m_is_redundant); | ||||||
|  |         add_def(def, ctx.get_literal(n)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::add_def(sat::literal def, sat::literal l) {         | ||||||
|  |         def_atom* a = new (get_region()) def_atom(l, def); | ||||||
|  |         insert_bv2a(l.var(), a); | ||||||
|  |         ctx.push(mk_atom_trail(l.var(), *this)); | ||||||
|  |         add_clause(l, ~def); | ||||||
|  |         add_clause(def, ~l); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_concat(app* n) { | ||||||
|  |         euf::enode* e = expr2enode(n); | ||||||
|  |         theory_var v = e->get_th_var(get_id()); | ||||||
|  |         m_bits[v].reset(); | ||||||
|  |         for (unsigned i = n->get_num_args(); i-- > 0; )  | ||||||
|  |             for (literal lit : m_bits[get_arg_var(e, i)])  | ||||||
|  |                 add_bit(v, lit);                     | ||||||
|  |         find_wpos(v); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_sub(app* n) { | ||||||
|  |         SASSERT(n->get_num_args() == 2); | ||||||
|  |         expr_ref_vector arg1_bits(m), arg2_bits(m), bits(m); | ||||||
|  |         get_arg_bits(n, 0, arg1_bits); | ||||||
|  |         get_arg_bits(n, 1, arg2_bits); | ||||||
|  |         SASSERT(arg1_bits.size() == arg2_bits.size()); | ||||||
|  |         expr_ref carry(m); | ||||||
|  |         m_bb.mk_subtracter(arg1_bits.size(), arg1_bits.c_ptr(), arg2_bits.c_ptr(), bits, carry); | ||||||
|  |         init_bits(n, bits); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_extract(app* n) { | ||||||
|  |         SASSERT(n->get_num_args() == 1); | ||||||
|  |         euf::enode* e = expr2enode(n); | ||||||
|  |         theory_var v = e->get_th_var(get_id()); | ||||||
|  |         theory_var arg = get_arg_var(e, 0); | ||||||
|  |         unsigned start = n->get_decl()->get_parameter(1).get_int(); | ||||||
|  |         unsigned end = n->get_decl()->get_parameter(0).get_int(); | ||||||
|  |         SASSERT(start <= end && end < get_bv_size(v)); | ||||||
|  |         m_bits[v].reset(); | ||||||
|  |         for (unsigned i = start; i <= end; ++i) | ||||||
|  |             add_bit(v, m_bits[arg][i]); | ||||||
|  |         find_wpos(v); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::internalize_bit2bool(app* n) { | ||||||
|  |         unsigned idx = 0; | ||||||
|  |         expr* arg = nullptr; | ||||||
|  |         VERIFY(bv.is_bit2bool(n, arg, idx)); | ||||||
|  |         euf::enode* argn = expr2enode(arg); | ||||||
|  |         if (!argn->is_attached_to(get_id())) { | ||||||
|  |             mk_var(argn); | ||||||
|  |         }         | ||||||
|  |         theory_var v_arg = argn->get_th_var(get_id()); | ||||||
|  |         sat::literal lit = expr2literal(n); | ||||||
|  |         sat::bool_var b = lit.var(); | ||||||
|  |         bit_atom* a = new (get_region()) bit_atom(); | ||||||
|  |         SASSERT(idx < get_bv_size(v_arg)); | ||||||
|  |         a->m_occs = new (get_region()) var_pos_occ(v_arg, idx); | ||||||
|  |         insert_bv2a(b, a); | ||||||
|  |         ctx.push(mk_atom_trail(b, *this));            | ||||||
|  |         if (idx < m_bits[v_arg].size() && lit != m_bits[v_arg][idx]) { | ||||||
|  |             add_clause(m_bits[v_arg][idx], ~lit); | ||||||
|  |             add_clause(~m_bits[v_arg][idx], lit); | ||||||
|  |         } | ||||||
|  |         // axiomatize bit2bool on constants.
 | ||||||
|  |         rational val; | ||||||
|  |         unsigned sz; | ||||||
|  |         if (bv.is_numeral(arg, val, sz)) { | ||||||
|  |             rational bit; | ||||||
|  |             div(val, rational::power_of_two(idx), bit); | ||||||
|  |             mod(bit, rational(2), bit); | ||||||
|  |             if (bit.is_zero()) lit.neg(); | ||||||
|  |             add_unit(lit); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::assert_ackerman(theory_var v1, theory_var v2) { | ||||||
|  |         flet<bool> _red(m_is_redundant, true); | ||||||
|  |         ++m_stats.m_ackerman; | ||||||
|  |         expr* o1 = var2expr(v1); | ||||||
|  |         expr* o2 = var2expr(v2); | ||||||
|  |         expr_ref oe(m.mk_eq(o1, o2), m); | ||||||
|  |         literal oeq = ctx.internalize(oe, false, false, m_is_redundant); | ||||||
|  |         unsigned sz = get_bv_size(v1); | ||||||
|  |         TRACE("bv", tout << oe << "\n";); | ||||||
|  |         literal_vector eqs; | ||||||
|  |         for (unsigned i = 0; i < sz; ++i) { | ||||||
|  |             literal l1 = m_bits[v1][i]; | ||||||
|  |             literal l2 = m_bits[v2][i]; | ||||||
|  |             expr_ref e1(m), e2(m); | ||||||
|  |             e1 = bv.mk_bit2bool(o1, i); | ||||||
|  |             e2 = bv.mk_bit2bool(o2, i); | ||||||
|  |             expr_ref e(m.mk_eq(e1, e2), m); | ||||||
|  |             literal eq = ctx.internalize(e, false, false, m_is_redundant); | ||||||
|  |             add_clause(l1, ~l2, ~eq); | ||||||
|  |             add_clause(~l1, l2, ~eq); | ||||||
|  |             add_clause(l1, l2, eq); | ||||||
|  |             add_clause(~l1, ~l2, eq); | ||||||
|  |             add_clause(eq, ~oeq); | ||||||
|  |             eqs.push_back(~eq); | ||||||
|  |         } | ||||||
|  |         eqs.push_back(oeq); | ||||||
|  |         s().add_clause(eqs.size(), eqs.c_ptr(), sat::status::th(m_is_redundant, get_id())); | ||||||
|  |     } | ||||||
| } | } | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  | @ -3,18 +3,20 @@ Copyright (c) 2020 Microsoft Corporation | ||||||
| 
 | 
 | ||||||
| Module Name: | Module Name: | ||||||
| 
 | 
 | ||||||
|     bv_internalize.cpp |     bv_solver.cpp | ||||||
| 
 | 
 | ||||||
| Abstract: | Abstract: | ||||||
| 
 | 
 | ||||||
|     Internalize utilities for bit-vector solver plugin. |     Solving utilities for bit-vectors. | ||||||
| 
 | 
 | ||||||
| Author: | Author: | ||||||
| 
 | 
 | ||||||
|     Nikolaj Bjorner (nbjorner) 2020-09-02 |     Nikolaj Bjorner (nbjorner) 2020-09-02 | ||||||
|  |     based on smt/theory_bv | ||||||
| 
 | 
 | ||||||
| --*/ | --*/ | ||||||
| 
 | 
 | ||||||
|  | #include "ast/ast_ll_pp.h" | ||||||
| #include "sat/smt/bv_solver.h" | #include "sat/smt/bv_solver.h" | ||||||
| #include "sat/smt/euf_solver.h" | #include "sat/smt/euf_solver.h" | ||||||
| #include "sat/smt/sat_th.h" | #include "sat/smt/sat_th.h" | ||||||
|  | @ -22,8 +24,73 @@ Author: | ||||||
| 
 | 
 | ||||||
| namespace bv { | namespace bv { | ||||||
| 
 | 
 | ||||||
|     void solver::fixed_var_eh(theory_var v) { |     class solver::bit_trail : public trail<euf::solver> { | ||||||
|  |         solver& s; | ||||||
|  |         solver::var_pos vp; | ||||||
|  |         sat::literal lit; | ||||||
|  |     public: | ||||||
|  |         bit_trail(solver& s, var_pos vp) : s(s), vp(vp), lit(s.m_bits[vp.first][vp.second]) {} | ||||||
| 
 | 
 | ||||||
|  |         virtual void undo(euf::solver& euf) { | ||||||
|  |             s.m_bits[vp.first][vp.second] = lit; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     solver::solver(euf::solver& ctx, theory_id id) : | ||||||
|  |         euf::th_euf_solver(ctx, id), | ||||||
|  |         bv(m), | ||||||
|  |         m_autil(m), | ||||||
|  |         m_ackerman(*this), | ||||||
|  |         m_bb(m, get_config()), | ||||||
|  |         m_find(*this) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::fixed_var_eh(theory_var v1) { | ||||||
|  |         numeral val1, val2; | ||||||
|  |         VERIFY(get_fixed_value(v1, val1)); | ||||||
|  |         unsigned sz = m_bits[v1].size(); | ||||||
|  |         value_sort_pair key(val1, sz); | ||||||
|  |         theory_var v2; | ||||||
|  |         bool is_current = | ||||||
|  |             m_fixed_var_table.find(key, v2) && | ||||||
|  |             v2 < static_cast<int>(get_num_vars()) && | ||||||
|  |             is_bv(v2) && | ||||||
|  |             get_bv_size(v2) == sz && | ||||||
|  |             get_fixed_value(v2, val2) && val1 == val2; | ||||||
|  | 
 | ||||||
|  |         if (!is_current) | ||||||
|  |             m_fixed_var_table.insert(key, v1); | ||||||
|  |         else if (var2enode(v1)->get_root() != var2enode(v2)->get_root()) { | ||||||
|  |             SASSERT(get_bv_size(v1) == get_bv_size(v2)); | ||||||
|  |             TRACE("bv", tout << "detected equality: v" << v1 << " = v" << v2 << "\n" << pp(v1) << pp(v2);); | ||||||
|  |             m_stats.m_num_th2core_eq++; | ||||||
|  |             add_fixed_eq(v1, v2); | ||||||
|  |             ctx.propagate(var2enode(v1), var2enode(v2), mk_bit2bv_justification(v1, v2)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::add_fixed_eq(theory_var v1, theory_var v2) { | ||||||
|  |         if (!get_config().m_bv_eq_axioms) | ||||||
|  |             return; | ||||||
|  |         m_ackerman.used_eq_eh(v1, v2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool solver::get_fixed_value(theory_var v, numeral& result) const { | ||||||
|  |         result.reset(); | ||||||
|  |         unsigned i = 0; | ||||||
|  |         for (literal b : m_bits[v]) { | ||||||
|  |             switch (ctx.s().value(b)) { | ||||||
|  |             case l_false: | ||||||
|  |                 break; | ||||||
|  |             case l_undef: | ||||||
|  |                 return false; | ||||||
|  |             case l_true: | ||||||
|  |                 result += power2(i); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             ++i; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  | @ -33,41 +100,34 @@ namespace bv { | ||||||
|         literal_vector const& bits = m_bits[v]; |         literal_vector const& bits = m_bits[v]; | ||||||
|         unsigned sz = bits.size(); |         unsigned sz = bits.size(); | ||||||
|         unsigned& wpos = m_wpos[v]; |         unsigned& wpos = m_wpos[v]; | ||||||
|         unsigned init               = wpos; |         for (unsigned i = 0; i < sz; ++i) { | ||||||
|         for (; wpos < sz; wpos++) { |             unsigned idx = (i + wpos) % sz; | ||||||
|             TRACE("find_wpos", tout << "curr bit: " << bits[wpos] << "\n";); |             if (s().value(bits[idx]) == l_undef) { | ||||||
|             if (s().value(bits[wpos]) == l_undef) { |                 wpos = idx; | ||||||
|                 TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); |                 TRACE("bv", tout << "moved wpos of v" << v << " to " << wpos << "\n";); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         wpos = 0; |         TRACE("bv", tout << "v" << v << " is a fixed variable.\n";); | ||||||
|         for (; wpos < init; wpos++) { |  | ||||||
|             if (s().value(bits[wpos]) == l_undef) { |  | ||||||
|                 TRACE("find_wpos", tout << "moved wpos of v" << v << " to " << wpos << "\n";); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         TRACE("find_wpos", tout << "v" << v << " is a fixed variable.\n";); |  | ||||||
|         fixed_var_eh(v); |         fixed_var_eh(v); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|    \brief v[idx] = ~v'[idx], then v /= v' is a theory axiom. |      *\brief v[idx] = ~v'[idx], then v /= v' is a theory axiom. | ||||||
|     */ |     */ | ||||||
|     void solver::find_new_diseq_axioms(var_pos_occ* occs, theory_var v, unsigned idx) { |     void solver::find_new_diseq_axioms(bit_atom& a, theory_var v, unsigned idx) { | ||||||
|  |         if (!get_config().m_bv_eq_axioms) | ||||||
|  |             return; | ||||||
|         literal l = m_bits[v][idx]; |         literal l = m_bits[v][idx]; | ||||||
|         l.neg(); |         l.neg(); | ||||||
|         while (occs) { |         for (auto vp : a) { | ||||||
|             theory_var v2 = occs->m_var; |             theory_var v2 = vp.first; | ||||||
|             unsigned   idx2 = occs->m_idx; |             unsigned   idx2 = vp.second; | ||||||
|             if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v)) |             if (idx == idx2 && m_bits[v2][idx2] == l && get_bv_size(v2) == get_bv_size(v)) | ||||||
|                 mk_new_diseq_axiom(v, v2, idx); |                 mk_new_diseq_axiom(v, v2, idx); | ||||||
|             occs = occs->m_next; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     /**
 |     /**
 | ||||||
|        \brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom. |        \brief v1[idx] = ~v2[idx], then v1 /= v2 is a theory axiom. | ||||||
|     */ |     */ | ||||||
|  | @ -78,60 +138,533 @@ namespace bv { | ||||||
|         // TBD: disabled until new literal creation is supported
 |         // TBD: disabled until new literal creation is supported
 | ||||||
|         return; |         return; | ||||||
|         SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]); |         SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]); | ||||||
|         TRACE("bv_solver", tout << "found new diseq axiom\n" << pp(v1) << pp(v2););  |         TRACE("bv", tout << "found new diseq axiom\n" << pp(v1) << pp(v2);); | ||||||
|         m_stats.m_num_diseq_static++; |         m_stats.m_num_diseq_static++; | ||||||
|         expr_ref eq(m.mk_eq(get_expr(v1), get_expr(v2)), m); |         expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m); | ||||||
|         sat::literal neq = ctx.internalize(eq, true, false, m_is_redundant); |         add_unit(~ctx.internalize(eq, false, false, m_is_redundant)); | ||||||
|         s().add_clause(1, &neq, sat::status::th(m_is_redundant, get_id())); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::ostream& solver::display(std::ostream& out, theory_var v) const { |     std::ostream& solver::display(std::ostream& out, theory_var v) const { | ||||||
|  |         expr* e = var2expr(v); | ||||||
|         out << "v"; |         out << "v"; | ||||||
|         out.width(4); |         out.width(4); | ||||||
|         out << std::left << v; |         out << std::left << v; | ||||||
|         out << " #"; |         out << " "; | ||||||
|         out.width(4); |         out.width(4); | ||||||
|         out << get_enode(v)->get_owner_id() << " -> #"; |         out << e->get_id() << " -> "; | ||||||
|         out.width(4); |         out.width(4); | ||||||
| #if 0 |         out << var2enode(find(v))->get_owner_id(); | ||||||
|         out << get_enode(find(v))->get_owner_id(); |         out << std::right; | ||||||
|         out << std::right << ", bits:"; |         out.flush(); | ||||||
|         literal_vector const& bits = m_bits[v]; |         atom* a = nullptr; | ||||||
|         for (literal lit : bits) { |         if (is_bv(v)) { | ||||||
|             out << " " << lit << ":"; |  | ||||||
|             ctx.display_literal(out, lit); |  | ||||||
|         } |  | ||||||
|             numeral val; |             numeral val; | ||||||
|             if (get_fixed_value(v, val)) |             if (get_fixed_value(v, val)) | ||||||
|             out << ", value: " << val; |                 out << " (= " << val << ")"; | ||||||
|  |             for (literal lit : m_bits[v]) { | ||||||
|  |                 out << " " << lit << ":" << mk_bounded_pp(literal2expr(lit), m, 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else if (m.is_bool(e) && (a = m_bool_var2atom.get(expr2literal(e).var(), nullptr))) { | ||||||
|  |             if (a->is_bit()) { | ||||||
|  |                 for (var_pos vp : a->to_bit()) | ||||||
|  |                     out << " " << var2enode(vp.first)->get_owner_id() << "[" << vp.second << "]"; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 out << "def-atom"; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             out << " " << mk_bounded_pp(e, m, 1); | ||||||
|         out << "\n"; |         out << "\n"; | ||||||
| #endif |  | ||||||
|         return out; |         return out; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void solver::new_eq_eh(euf::th_eq const& eq) { | ||||||
|  |         TRACE("bv", tout << "new eq " << eq.m_v1 << " == " << eq.m_v2 << "\n";); | ||||||
|  |         if (is_bv(eq.m_v1)) | ||||||
|  |             m_find.merge(eq.m_v1, eq.m_v2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; } |     double solver::get_reward(literal l, sat::ext_constraint_idx idx, sat::literal_occs_fun& occs) const { return 0; } | ||||||
|     bool solver::is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) { return false; } |     bool solver::is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) { return false; } | ||||||
|     bool solver::is_external(bool_var v) { return true; } |     bool solver::is_external(bool_var v) { return true; } | ||||||
|     bool solver::propagate(literal l, sat::ext_constraint_idx idx) { return false; } |     bool solver::propagate(literal l, sat::ext_constraint_idx idx) { return false; } | ||||||
|     void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r) {} | 
 | ||||||
|     void solver::asserted(literal l) {} |     void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector& r, bool probing) { | ||||||
|     sat::check_result solver::check() { return sat::check_result::CR_DONE; } |         auto& c = bv_justification::from_index(idx); | ||||||
|     void solver::push() {} |         TRACE("bv", display_constraint(tout, idx);); | ||||||
|     void solver::pop(unsigned n) {} |         switch (c.m_kind) { | ||||||
|  |         case bv_justification::kind_t::bv2bit: | ||||||
|  |             r.push_back(c.m_antecedent); | ||||||
|  |             ctx.add_antecedent(var2enode(c.m_v1), var2enode(c.m_v2)); | ||||||
|  |             break; | ||||||
|  |         case bv_justification::kind_t::bit2bv: | ||||||
|  |             SASSERT(m_bits[c.m_v1].size() == m_bits[c.m_v2].size()); | ||||||
|  |             for (unsigned i = m_bits[c.m_v1].size(); i-- > 0; ) { | ||||||
|  |                 sat::literal a = m_bits[c.m_v1][i]; | ||||||
|  |                 sat::literal b = m_bits[c.m_v2][i]; | ||||||
|  |                 SASSERT(a == b || s().value(a) != l_undef); | ||||||
|  |                 SASSERT(s().value(a) == s().value(b)); | ||||||
|  |                 if (a == b) | ||||||
|  |                     continue; | ||||||
|  |                 if (s().value(a) == l_false) { | ||||||
|  |                     a.neg(); | ||||||
|  |                     b.neg(); | ||||||
|  |                 } | ||||||
|  |                 r.push_back(a); | ||||||
|  |                 r.push_back(b); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (!probing && ctx.use_drat()) | ||||||
|  |             log_drat(c); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::log_drat(bv_justification const& c) { | ||||||
|  |         // this has a side-effect so changes provability:
 | ||||||
|  |         expr_ref eq(m.mk_eq(var2expr(c.m_v1), var2expr(c.m_v2)), m); | ||||||
|  |         sat::literal leq = ctx.internalize(eq, false, false, false); | ||||||
|  |         sat::literal_vector lits; | ||||||
|  |         auto add_bit = [&](sat::literal lit) { | ||||||
|  |             if (s().value(lit) == l_true) | ||||||
|  |                 lit.neg(); | ||||||
|  |             lits.push_back(lit); | ||||||
|  |         }; | ||||||
|  |         switch (c.m_kind) { | ||||||
|  |         case bv_justification::kind_t::bv2bit: | ||||||
|  |             lits.push_back(~leq); | ||||||
|  |             lits.push_back(~c.m_antecedent); | ||||||
|  |             lits.push_back(c.m_consequent); | ||||||
|  |             break; | ||||||
|  |         case bv_justification::kind_t::bit2bv: | ||||||
|  |             lits.push_back(leq); | ||||||
|  |             for (unsigned i = m_bits[c.m_v1].size(); i-- > 0; ) { | ||||||
|  |                 sat::literal a = m_bits[c.m_v1][i]; | ||||||
|  |                 sat::literal b = m_bits[c.m_v2][i]; | ||||||
|  |                 if (a != b) { | ||||||
|  |                     add_bit(a); | ||||||
|  |                     add_bit(b); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         ctx.get_drat().add(lits, status()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::asserted(literal l) { | ||||||
|  |         atom* a = get_bv2a(l.var()); | ||||||
|  |         TRACE("bv", tout << "asserted: " << l << "\n";); | ||||||
|  |         if (a->is_bit()) | ||||||
|  |             for (auto vp : a->to_bit()) | ||||||
|  |                 m_prop_queue.push_back(vp); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool solver::unit_propagate() { | ||||||
|  |         if (m_prop_queue_head == m_prop_queue.size()) | ||||||
|  |             return false; | ||||||
|  |         ctx.push(value_trail<euf::solver, unsigned>(m_prop_queue_head)); | ||||||
|  |         for (; m_prop_queue_head < m_prop_queue.size() && !s().inconsistent(); ++m_prop_queue_head) | ||||||
|  |             propagate_bits(m_prop_queue[m_prop_queue_head]); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::propagate_bits(var_pos entry) { | ||||||
|  |         theory_var v1 = entry.first; | ||||||
|  |         unsigned idx = entry.second; | ||||||
|  |         SASSERT(idx < m_bits[v1].size()); | ||||||
|  |         if (m_wpos[v1] == idx) | ||||||
|  |             find_wpos(v1); | ||||||
|  | 
 | ||||||
|  |         literal bit1 = m_bits[v1][idx]; | ||||||
|  |         lbool   val = s().value(bit1); | ||||||
|  |         TRACE("bv", tout << "propagating v" << v1 << " #" << var2enode(v1)->get_owner_id() << "[" << idx << "] = " << val << "\n";); | ||||||
|  |         if (val == l_undef) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         if (val == l_false) | ||||||
|  |             bit1.neg(); | ||||||
|  | 
 | ||||||
|  |         for (theory_var v2 = m_find.next(v1); v2 != v1 && !s().inconsistent(); v2 = m_find.next(v2)) { | ||||||
|  |             literal bit2 = m_bits[v2][idx]; | ||||||
|  |             SASSERT(m_bits[v1][idx] != ~m_bits[v2][idx]); | ||||||
|  |             TRACE("bv", tout << "propagating #" << var2enode(v2)->get_owner_id() << "[" << idx << "] = " << s().value(bit2) << "\n";); | ||||||
|  | 
 | ||||||
|  |             if (val == l_false) | ||||||
|  |                 bit2.neg(); | ||||||
|  |             if (l_true != s().value(bit2)) | ||||||
|  |                 assign_bit(bit2, v1, v2, idx, bit1, false); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sat::check_result solver::check() { | ||||||
|  |         SASSERT(m_prop_queue.size() == m_prop_queue_head); | ||||||
|  |         return sat::check_result::CR_DONE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::push() { | ||||||
|  |         th_euf_solver::lazy_push(); | ||||||
|  |         m_prop_queue_lim.push_back(m_prop_queue.size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::pop(unsigned n) { | ||||||
|  |         unsigned old_sz = m_prop_queue_lim.size() - n; | ||||||
|  |         m_prop_queue.shrink(m_prop_queue_lim[old_sz]); | ||||||
|  |         m_prop_queue_lim.shrink(old_sz); | ||||||
|  |         n = lazy_pop(n); | ||||||
|  |         if (n > 0) { | ||||||
|  |             old_sz = get_num_vars(); | ||||||
|  |             m_bits.shrink(old_sz); | ||||||
|  |             m_wpos.shrink(old_sz); | ||||||
|  |             m_zero_one_bits.shrink(old_sz); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void solver::pre_simplify() {} |     void solver::pre_simplify() {} | ||||||
|     void solver::simplify() {} | 
 | ||||||
|  |     void solver::simplify() { | ||||||
|  |         m_ackerman.propagate(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool solver::set_root(literal l, literal r) { | ||||||
|  |         atom* a = get_bv2a(l.var()); | ||||||
|  |         if (!a || !a->is_bit()) | ||||||
|  |             return true; | ||||||
|  |         for (auto vp : a->to_bit()) { | ||||||
|  |             sat::literal l2 = m_bits[vp.first][vp.second]; | ||||||
|  |             sat::literal r2 = (l.sign() == l2.sign()) ? r : ~r; | ||||||
|  |             SASSERT(l2.var() == l.var()); | ||||||
|  |             ctx.push(bit_trail(*this, vp)); | ||||||
|  |             m_bits[vp.first][vp.second] = r2; | ||||||
|  |             set_bit_eh(vp.first, r2, vp.second); | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     * Instantiate Ackerman axioms for bit-vectors that have become equal after roots have been added. | ||||||
|  |     */ | ||||||
|  |     void solver::flush_roots() { | ||||||
|  |         struct eq { | ||||||
|  |             solver& s; | ||||||
|  |             eq(solver& s) :s(s) {} | ||||||
|  |             bool operator()(theory_var v1, theory_var v2) const { | ||||||
|  |                 return s.m_bits[v1] == s.m_bits[v2]; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         struct hash { | ||||||
|  |             solver& s; | ||||||
|  |             hash(solver& s) :s(s) {} | ||||||
|  |             bool operator()(theory_var v) const { | ||||||
|  |                 literal_vector const& a = s.m_bits[v]; | ||||||
|  |                 return string_hash(reinterpret_cast<char*>(a.c_ptr()), a.size() * sizeof(sat::literal), 3); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         eq eq_proc(*this); | ||||||
|  |         hash hash_proc(*this); | ||||||
|  |         map<theory_var, theory_var, hash, eq> table(hash_proc, eq_proc); | ||||||
|  |         for (unsigned v = 0; v < get_num_vars(); ++v) { | ||||||
|  |             if (!m_bits[v].empty()) { | ||||||
|  |                 theory_var w = table.insert_if_not_there(v, v); | ||||||
|  |                 if (v != w && m_find.find(v) != m_find.find(w)) | ||||||
|  |                     assert_ackerman(v, w); | ||||||
|  |             } | ||||||
|  |         }     | ||||||
|  |         TRACE("bv", tout << "infer new equations for bit-vectors that are now equal\n";); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void solver::clauses_modifed() {} |     void solver::clauses_modifed() {} | ||||||
|     lbool solver::get_phase(bool_var v) { return l_undef; } |     lbool solver::get_phase(bool_var v) { return l_undef; } | ||||||
|     std::ostream& solver::display(std::ostream& out) const { return out; } |     std::ostream& solver::display(std::ostream& out) const { | ||||||
|     std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; } |         unsigned num_vars = get_num_vars(); | ||||||
|     std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; } |         if (num_vars > 0) | ||||||
|     void solver::collect_statistics(statistics& st) const {} |             out << "bv-solver:\n"; | ||||||
|     sat::extension* solver::copy(sat::solver* s) { return nullptr; } |         for (unsigned v = 0; v < num_vars; v++) | ||||||
|  |             out << pp(v); | ||||||
|  |         return out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { | ||||||
|  |         return display_constraint(out, idx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { | ||||||
|  |         auto& c = bv_justification::from_index(idx); | ||||||
|  |         switch (c.m_kind) { | ||||||
|  |         case bv_justification::kind_t::bv2bit: | ||||||
|  |             return out << c.m_consequent << " <= " << c.m_antecedent << " v" << c.m_v1 << " == v" << c.m_v2 << "\n"; | ||||||
|  |         case bv_justification::kind_t::bit2bv: | ||||||
|  |             return out << m_bits[c.m_v1] << " == " << m_bits[c.m_v2] << " => v" << c.m_v1 << " == v" << c.m_v2 << "\n"; | ||||||
|  |         default: | ||||||
|  |             UNREACHABLE(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         return out; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::collect_statistics(statistics& st) const { | ||||||
|  |         st.update("bv conflicts", m_stats.m_num_conflicts); | ||||||
|  |         st.update("bv diseqs", m_stats.m_num_diseq_static); | ||||||
|  |         st.update("bv dynamic diseqs", m_stats.m_num_diseq_dynamic); | ||||||
|  |         st.update("bv bit2core", m_stats.m_num_bit2core); | ||||||
|  |         st.update("bv->core eq", m_stats.m_num_th2core_eq); | ||||||
|  |         st.update("bv ackerman", m_stats.m_ackerman); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sat::extension* solver::copy(sat::solver* s) { UNREACHABLE(); return nullptr; } | ||||||
|  | 
 | ||||||
|  |     euf::th_solver* solver::fresh(sat::solver* s, euf::solver& ctx) { | ||||||
|  |         bv::solver* result = alloc(bv::solver, ctx, get_id()); | ||||||
|  |         ast_translation tr(m, ctx.get_manager()); | ||||||
|  |         for (unsigned i = 0; i < get_num_vars(); ++i) { | ||||||
|  |             expr* e1 = var2expr(i); | ||||||
|  |             expr* e2 = tr(e1); | ||||||
|  |             euf::enode* n2 = ctx.get_enode(e2); | ||||||
|  |             SASSERT(n2); | ||||||
|  |             result->mk_var(n2); | ||||||
|  |             result->m_bits[i].append(m_bits[i]); | ||||||
|  |             result->m_zero_one_bits[i].append(m_zero_one_bits[i]); | ||||||
|  |         } | ||||||
|  |         for (unsigned i = 0; i < get_num_vars(); ++i) | ||||||
|  |             if (find(i) != i) | ||||||
|  |                 result->m_find.merge(i, find(i)); | ||||||
|  |         result->m_prop_queue.append(m_prop_queue); | ||||||
|  |         for (unsigned i = 0; i < m_bool_var2atom.size(); ++i) { | ||||||
|  |             atom* a = m_bool_var2atom[i]; | ||||||
|  |             if (!a) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |             if (a->is_bit()) { | ||||||
|  |                 bit_atom* new_a = new (result->get_region()) bit_atom(); | ||||||
|  |                 m_bool_var2atom.setx(i, new_a, nullptr); | ||||||
|  |                 for (auto vp : a->to_bit()) | ||||||
|  |                     new_a->m_occs = new (result->get_region()) var_pos_occ(vp.first, vp.second, new_a->m_occs); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 def_atom* new_a = new (result->get_region()) def_atom(a->to_def().m_var, a->to_def().m_def); | ||||||
|  |                 m_bool_var2atom.setx(i, new_a, nullptr); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void solver::pop_reinit() {} |     void solver::pop_reinit() {} | ||||||
|     bool solver::validate() { return true; } |     bool solver::validate() { return true; } | ||||||
|     void solver::init_use_list(sat::ext_use_list& ul) {} |     void solver::init_use_list(sat::ext_use_list& ul) {} | ||||||
|     bool solver::is_blocked(literal l, sat::ext_constraint_idx) { return false; } |     bool solver::is_blocked(literal l, sat::ext_constraint_idx) { return false; } | ||||||
|     bool solver::check_model(sat::model const& m) const { return true; } |     bool solver::check_model(sat::model const& m) const { return true; } | ||||||
|     unsigned solver::max_var(unsigned w) const { return 0;} |     unsigned solver::max_var(unsigned w) const { return w; } | ||||||
| 
 | 
 | ||||||
|  |     void solver::add_value(euf::enode* n, expr_ref_vector& values) { | ||||||
|  |         SASSERT(bv.is_bv(n->get_owner())); | ||||||
|  |         theory_var v = n->get_th_var(get_id()); | ||||||
|  |         rational val; | ||||||
|  |         unsigned i = 0; | ||||||
|  |         for (auto l : m_bits[v]) { | ||||||
|  |             switch (s().value(l)) { | ||||||
|  |             case l_true: | ||||||
|  |                 val += power2(i); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             ++i; | ||||||
|  |         } | ||||||
|  |         values[n->get_root_id()] = bv.mk_numeral(val, m_bits[v].size()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     trail_stack<euf::solver>& solver::get_trail_stack() { | ||||||
|  |         return ctx.get_trail_stack(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { | ||||||
|  |         TRACE("bv", tout << "merging: v" << v1 << " #" << var2enode(v1)->get_owner_id() << " v" << v2 << " #" << var2enode(v2)->get_owner_id() << "\n";); | ||||||
|  |         if (!merge_zero_one_bits(r1, r2)) { | ||||||
|  |             TRACE("bv", tout << "conflict detected\n";); | ||||||
|  |             return; // conflict was detected
 | ||||||
|  |         } | ||||||
|  |         SASSERT(m_bits[v1].size() == m_bits[v2].size()); | ||||||
|  |         unsigned sz = m_bits[v1].size(); | ||||||
|  |         for (unsigned idx = 0; !s().inconsistent() && idx < sz; idx++) { | ||||||
|  |             literal bit1 = m_bits[v1][idx]; | ||||||
|  |             literal bit2 = m_bits[v2][idx]; | ||||||
|  |             CTRACE("bv", bit1 == ~bit2, tout << pp(v1) << pp(v2) << "idx: " << idx << "\n";); | ||||||
|  |             SASSERT(bit1 != ~bit2); | ||||||
|  |             lbool val1 = s().value(bit1); | ||||||
|  |             lbool val2 = s().value(bit2); | ||||||
|  |             TRACE("bv", tout << "merge v" << v1 << " " << bit1 << ":= " << val1 << " " << bit2 << ":= " << val2 << "\n";); | ||||||
|  |             if (val1 == val2) | ||||||
|  |                 continue; | ||||||
|  |             CTRACE("bv", (val1 != l_undef && val2 != l_undef), tout << "inconsistent "; tout << pp(v1) << pp(v2) << "idx: " << idx << "\n";); | ||||||
|  |             if (val1 == l_false) | ||||||
|  |                 assign_bit(~bit2, v1, v2, idx, ~bit1, true); | ||||||
|  |             else if (val1 == l_true) | ||||||
|  |                 assign_bit(bit2, v1, v2, idx, bit1, true); | ||||||
|  |             else if (val2 == l_false) | ||||||
|  |                 assign_bit(~bit1, v2, v1, idx, ~bit2, true); | ||||||
|  |             else if (val2 == l_true) | ||||||
|  |                 assign_bit(bit1, v2, v1, idx, bit2, true); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sat::justification solver::mk_bv2bit_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a) { | ||||||
|  |         void* mem = get_region().allocate(bv_justification::get_obj_size()); | ||||||
|  |         sat::constraint_base::initialize(mem, this); | ||||||
|  |         auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2, c, a); | ||||||
|  |         return sat::justification::mk_ext_justification(s().scope_lvl(), constraint->to_index()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sat::ext_justification_idx solver::mk_bit2bv_justification(theory_var v1, theory_var v2) { | ||||||
|  |         void* mem = get_region().allocate(bv_justification::get_obj_size()); | ||||||
|  |         sat::constraint_base::initialize(mem, this); | ||||||
|  |         auto* constraint = new (sat::constraint_base::ptr2mem(mem)) bv_justification(v1, v2); | ||||||
|  |         return constraint->to_index(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc) { | ||||||
|  |         m_stats.m_num_bit2core++; | ||||||
|  |         SASSERT(ctx.s().value(antecedent) == l_true); | ||||||
|  |         SASSERT(m_bits[v2][idx].var() == consequent.var()); | ||||||
|  |         SASSERT(consequent.var() != antecedent.var()); | ||||||
|  |         s().assign(consequent, mk_bv2bit_justification(v1, v2, consequent, antecedent)); | ||||||
|  |         if (s().value(consequent) == l_false) { | ||||||
|  |             m_stats.m_num_conflicts++; | ||||||
|  |             SASSERT(s().inconsistent()); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             if (get_config().m_bv_eq_axioms && false) { | ||||||
|  |                 // TODO - enable when pop_reinit is available
 | ||||||
|  |                 expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m); | ||||||
|  |                 flet<bool> _is_redundant(m_is_redundant, true); | ||||||
|  |                 literal eq_lit = ctx.internalize(eq, false, false, m_is_redundant); | ||||||
|  |                 add_clause(~antecedent, ~eq_lit, consequent); | ||||||
|  |                 add_clause(antecedent, ~eq_lit, ~consequent); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (m_wpos[v2] == idx) | ||||||
|  |                 find_wpos(v2); | ||||||
|  |             bool_var cv = consequent.var(); | ||||||
|  |             atom* a = get_bv2a(cv); | ||||||
|  |             if (a && a->is_bit()) | ||||||
|  |                 for (auto curr : a->to_bit()) | ||||||
|  |                     if (propagate_eqc || find(curr.first) != find(v2) || curr.second != idx)  | ||||||
|  |                         m_prop_queue.push_back(curr);                     | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::unmerge_eh(theory_var v1, theory_var v2) { | ||||||
|  |         // v1 was the root of the equivalence class
 | ||||||
|  |         // I must remove the zero_one_bits that are from v2.
 | ||||||
|  |         zero_one_bits& bits = m_zero_one_bits[v1]; | ||||||
|  |         if (bits.empty()) | ||||||
|  |             return; | ||||||
|  |         for (unsigned j = bits.size(); j-- > 0; ) { | ||||||
|  |             zero_one_bit& bit = bits[j]; | ||||||
|  |             if (find(bit.m_owner) == v1) { | ||||||
|  |                 bits.shrink(j + 1); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         bits.shrink(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool solver::merge_zero_one_bits(theory_var r1, theory_var r2) { | ||||||
|  |         zero_one_bits& bits2 = m_zero_one_bits[r2]; | ||||||
|  |         if (bits2.empty()) | ||||||
|  |             return true; | ||||||
|  |         zero_one_bits& bits1 = m_zero_one_bits[r1]; | ||||||
|  |         unsigned bv_size = get_bv_size(r1); | ||||||
|  |         SASSERT(bv_size == get_bv_size(r2)); | ||||||
|  |         m_merge_aux[0].reserve(bv_size + 1, euf::null_theory_var); | ||||||
|  |         m_merge_aux[1].reserve(bv_size + 1, euf::null_theory_var); | ||||||
|  | 
 | ||||||
|  |         struct scoped_reset { | ||||||
|  |             solver& s; | ||||||
|  |             zero_one_bits& bits1; | ||||||
|  |             scoped_reset(solver& s, zero_one_bits& bits1) :s(s), bits1(bits1) {} | ||||||
|  |             ~scoped_reset() { | ||||||
|  |                 for (auto& zo : bits1) | ||||||
|  |                     s.m_merge_aux[zo.m_is_true][zo.m_idx] = euf::null_theory_var; | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         scoped_reset _sr(*this, bits1); | ||||||
|  | 
 | ||||||
|  |         DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) SASSERT(m_merge_aux[0][i] == euf::null_theory_var || m_merge_aux[1][i] == euf::null_theory_var);); | ||||||
|  | 
 | ||||||
|  |         // save info about bits1
 | ||||||
|  |         for (auto& zo : bits1) | ||||||
|  |             m_merge_aux[zo.m_is_true][zo.m_idx] = zo.m_owner; | ||||||
|  |         // check if bits2 is consistent with bits1, and copy new bits to bits1
 | ||||||
|  |         for (auto& zo : bits2) { | ||||||
|  |             theory_var v2 = zo.m_owner; | ||||||
|  |             theory_var v1 = m_merge_aux[!zo.m_is_true][zo.m_idx]; | ||||||
|  |             if (v1 != euf::null_theory_var) { | ||||||
|  |                 // conflict was detected ... v1 and v2 have complementary bits
 | ||||||
|  |                 SASSERT(m_bits[v1][zo.m_idx] == ~(m_bits[v2][zo.m_idx])); | ||||||
|  |                 SASSERT(m_bits[v1].size() == m_bits[v2].size()); | ||||||
|  |                 mk_new_diseq_axiom(v1, v2, zo.m_idx); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             // copy missing variable to bits1
 | ||||||
|  |             if (m_merge_aux[zo.m_is_true][zo.m_idx] == euf::null_theory_var) | ||||||
|  |                 bits1.push_back(zo); | ||||||
|  |         } | ||||||
|  |         // reset m_merge_aux vector
 | ||||||
|  |         DEBUG_CODE(for (unsigned i = 0; i < bv_size; i++) { SASSERT(m_merge_aux[0][i] == euf::null_theory_var || m_merge_aux[1][i] == euf::null_theory_var); }); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rational const& solver::power2(unsigned i) const { | ||||||
|  |         while (m_power2.size() <= i) | ||||||
|  |             m_power2.push_back(m_bb.power(m_power2.size())); | ||||||
|  |         return m_power2[i]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         \brief Check whether m_zero_one_bits is an accurate summary of the bits in the | ||||||
|  |         equivalence class rooted by v. | ||||||
|  |         \remark The method does nothing if v is not the root of the equivalence class. | ||||||
|  |     */ | ||||||
|  |     bool solver::check_zero_one_bits(theory_var v) { | ||||||
|  |         if (s().inconsistent()) | ||||||
|  |             return true; // property is only valid if the context is not in a conflict.
 | ||||||
|  |         if (!is_root(v) || !is_bv(v)) | ||||||
|  |             return true; | ||||||
|  |         bool_vector bits[2]; | ||||||
|  |         unsigned      num_bits = 0; | ||||||
|  |         unsigned      bv_sz = get_bv_size(v); | ||||||
|  |         bits[0].resize(bv_sz, false); | ||||||
|  |         bits[1].resize(bv_sz, false); | ||||||
|  | 
 | ||||||
|  |         theory_var curr = v; | ||||||
|  |         do { | ||||||
|  |             literal_vector const& lits = m_bits[curr]; | ||||||
|  |             for (unsigned i = 0; i < lits.size(); i++) { | ||||||
|  |                 literal l = lits[i]; | ||||||
|  |                 if (s().value(l) != l_undef) { | ||||||
|  |                     unsigned is_true = s().value(l) == l_true; | ||||||
|  |                     if (bits[!is_true][i]) { | ||||||
|  |                         // expect a conflict later on.
 | ||||||
|  |                         return true; | ||||||
|  |                     } | ||||||
|  |                     if (!bits[is_true][i]) { | ||||||
|  |                         bits[is_true][i] = true; | ||||||
|  |                         num_bits++; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             curr = m_find.next(curr); | ||||||
|  |         } while (curr != v); | ||||||
|  | 
 | ||||||
|  |         zero_one_bits const& _bits = m_zero_one_bits[v]; | ||||||
|  |         SASSERT(_bits.size() == num_bits); | ||||||
|  |         bool_vector already_found; | ||||||
|  |         already_found.resize(bv_sz, false); | ||||||
|  |         for (auto& zo : _bits) { | ||||||
|  |             SASSERT(find(zo.m_owner) == v); | ||||||
|  |             SASSERT(bits[zo.m_is_true][zo.m_idx]); | ||||||
|  |             SASSERT(!already_found[zo.m_idx]); | ||||||
|  |             already_found[zo.m_idx] = true; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -17,8 +17,13 @@ Author: | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "sat/smt/sat_th.h" | #include "sat/smt/sat_th.h" | ||||||
|  | #include "sat/smt/bv_ackerman.h" | ||||||
| #include "ast/rewriter/bit_blaster/bit_blaster.h" | #include "ast/rewriter/bit_blaster/bit_blaster.h" | ||||||
| 
 | 
 | ||||||
|  | namespace euf { | ||||||
|  |     class solver; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| namespace bv { | namespace bv { | ||||||
| 
 | 
 | ||||||
|     class solver : public euf::th_euf_solver { |     class solver : public euf::th_euf_solver { | ||||||
|  | @ -32,15 +37,42 @@ namespace bv { | ||||||
|         typedef std::pair<numeral, unsigned> value_sort_pair; |         typedef std::pair<numeral, unsigned> value_sort_pair; | ||||||
|         typedef pair_hash<obj_hash<numeral>, unsigned_hash> value_sort_pair_hash; |         typedef pair_hash<obj_hash<numeral>, unsigned_hash> value_sort_pair_hash; | ||||||
|         typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var; |         typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var; | ||||||
|         typedef union_find<solver>  th_union_find; |         typedef union_find<solver, euf::solver>  bv_union_find; | ||||||
|  |         typedef std::pair<theory_var, unsigned> var_pos; | ||||||
|  | 
 | ||||||
|  |         friend class ackerman; | ||||||
| 
 | 
 | ||||||
|         struct stats { |         struct stats { | ||||||
|             unsigned   m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts; |             unsigned   m_num_diseq_static, m_num_diseq_dynamic, m_num_bit2core, m_num_th2core_eq, m_num_conflicts; | ||||||
|             unsigned   m_num_eq_dynamic; |             unsigned   m_ackerman; | ||||||
|             void reset() { memset(this, 0, sizeof(stats)); } |             void reset() { memset(this, 0, sizeof(stats)); } | ||||||
|             stats() { reset(); } |             stats() { reset(); } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  |         struct bv_justification { | ||||||
|  |             enum kind_t { bv2bit, bit2bv }; | ||||||
|  |             kind_t     m_kind; | ||||||
|  |             theory_var m_v1; | ||||||
|  |             theory_var m_v2; | ||||||
|  |             sat::literal m_consequent; | ||||||
|  |             sat::literal m_antecedent; | ||||||
|  |             bv_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a) : | ||||||
|  |                 m_kind(kind_t::bv2bit), m_v1(v1), m_v2(v2), m_consequent(c), m_antecedent(a) {} | ||||||
|  |             bv_justification(theory_var v1, theory_var v2): | ||||||
|  |                 m_kind(kind_t::bit2bv), m_v1(v1), m_v2(v2) {} | ||||||
|  |             sat::ext_constraint_idx to_index() const {  | ||||||
|  |                 return sat::constraint_base::mem2base(this);  | ||||||
|  |             } | ||||||
|  |             static bv_justification& from_index(size_t idx) { | ||||||
|  |                 return *reinterpret_cast<bv_justification*>(sat::constraint_base::from_index(idx)->mem()); | ||||||
|  |             } | ||||||
|  |             static size_t get_obj_size() { return sat::constraint_base::obj_size(sizeof(bv_justification)); } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         sat::justification mk_bv2bit_justification(theory_var v1, theory_var v2, sat::literal c, sat::literal a); | ||||||
|  |         sat::ext_justification_idx mk_bit2bv_justification(theory_var v1, theory_var v2); | ||||||
|  |         void log_drat(bv_justification const& c); | ||||||
|  |   | ||||||
| 
 | 
 | ||||||
|         /**
 |         /**
 | ||||||
|            \brief Structure used to store the position of a bitvector variable that |            \brief Structure used to store the position of a bitvector variable that | ||||||
|  | @ -66,17 +98,31 @@ namespace bv { | ||||||
| 
 | 
 | ||||||
|         typedef svector<zero_one_bit> zero_one_bits; |         typedef svector<zero_one_bit> zero_one_bits; | ||||||
| 
 | 
 | ||||||
|  |         struct bit_atom; | ||||||
|  |         struct def_atom; | ||||||
|         class atom { |         class atom { | ||||||
|         public: |         public: | ||||||
|             virtual ~atom() {} |             virtual ~atom() {} | ||||||
|             virtual bool is_bit() const = 0; |             virtual bool is_bit() const = 0; | ||||||
|  |             bit_atom& to_bit(); | ||||||
|  |             def_atom& to_def(); | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         struct var_pos_occ { |         struct var_pos_occ { | ||||||
|             theory_var    m_var; |             var_pos       m_vp; | ||||||
|             unsigned      m_idx; |  | ||||||
|             var_pos_occ * m_next; |             var_pos_occ * m_next; | ||||||
|             var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_var(v), m_idx(idx), m_next(next) {} |             var_pos_occ(theory_var v = euf::null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_vp(v, idx), m_next(next) {} | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         class var_pos_it { | ||||||
|  |             var_pos_occ* m_first; | ||||||
|  |         public: | ||||||
|  |             var_pos_it(var_pos_occ* c) : m_first(c) {} | ||||||
|  |             var_pos operator*() { return m_first->m_vp; } | ||||||
|  |             var_pos_it& operator++() { m_first = m_first->m_next; return *this; } | ||||||
|  |             var_pos_it operator++(int) { var_pos_it tmp = *this; ++* this; return tmp; } | ||||||
|  |             bool operator==(var_pos_it const& other) const { return m_first == other.m_first; } | ||||||
|  |             bool operator!=(var_pos_it const& other) const { return !(*this == other); } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         struct bit_atom : public atom { |         struct bit_atom : public atom { | ||||||
|  | @ -84,44 +130,51 @@ namespace bv { | ||||||
|             bit_atom():m_occs(nullptr) {} |             bit_atom():m_occs(nullptr) {} | ||||||
|             ~bit_atom() override {} |             ~bit_atom() override {} | ||||||
|             bool is_bit() const override { return true; } |             bool is_bit() const override { return true; } | ||||||
|  |             var_pos_it begin() const { return var_pos_it(m_occs); } | ||||||
|  |             var_pos_it end() const { return var_pos_it(nullptr); } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         struct le_atom : public atom { |         struct def_atom : public atom { | ||||||
|             literal    m_var; |             literal    m_var; | ||||||
|             literal    m_def; |             literal    m_def; | ||||||
|             le_atom(literal v, literal d):m_var(v), m_def(d) {} |             def_atom(literal v, literal d):m_var(v), m_def(d) {} | ||||||
|             ~le_atom() override {} |             ~def_atom() override {} | ||||||
|             bool is_bit() const override { return false; } |             bool is_bit() const override { return false; } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         friend class add_var_pos_trail; |         class bit_trail; | ||||||
|         friend class mk_atom_trail; |         class add_var_pos_trail; | ||||||
|  |         class mk_atom_trail; | ||||||
|         typedef ptr_vector<atom> bool_var2atom; |         typedef ptr_vector<atom> bool_var2atom; | ||||||
| 
 | 
 | ||||||
|         bv_util                  bv; |         bv_util                  bv; | ||||||
|         arith_util               m_autil; |         arith_util               m_autil; | ||||||
|         stats                    m_stats; |         stats                    m_stats; | ||||||
|  |         ackerman                 m_ackerman; | ||||||
|         bit_blaster              m_bb; |         bit_blaster              m_bb; | ||||||
|         th_union_find            m_find; |         bv_union_find            m_find; | ||||||
|         vector<literal_vector>   m_bits;     // per var, the bits of a given variable.
 |         vector<literal_vector>   m_bits;     // per var, the bits of a given variable.
 | ||||||
|         ptr_vector<expr>         m_bits_expr; |         unsigned_vector          m_wpos;     // per var, watch position for fixed variable detection. 
 | ||||||
|         svector<unsigned>        m_wpos;     // per var, watch position for fixed variable detection. 
 |  | ||||||
|         vector<zero_one_bits>    m_zero_one_bits; // per var, see comment in the struct zero_one_bit
 |         vector<zero_one_bits>    m_zero_one_bits; // per var, see comment in the struct zero_one_bit
 | ||||||
|         bool_var2atom            m_bool_var2atom; |         bool_var2atom            m_bool_var2atom; | ||||||
|  |         value2var                m_fixed_var_table; | ||||||
|  |         mutable vector<rational> m_power2; | ||||||
|  |         literal_vector           m_tmp_literals; | ||||||
|  |         svector<var_pos>         m_prop_queue; | ||||||
|  |         unsigned_vector          m_prop_queue_lim; | ||||||
|  |         unsigned                 m_prop_queue_head { 0 }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|         sat::solver* m_solver; |         sat::solver* m_solver; | ||||||
|         sat::solver& s() { return *m_solver;  } |         sat::solver& s() { return *m_solver;  } | ||||||
| 
 | 
 | ||||||
|         // internalize:
 |         // internalize
 | ||||||
| 
 |  | ||||||
|         void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); } |         void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); } | ||||||
|         void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; } |         void erase_bv2a(bool_var bv) { m_bool_var2atom[bv] = 0; } | ||||||
|         atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); } |         atom * get_bv2a(bool_var bv) const { return m_bool_var2atom.get(bv, 0); } | ||||||
| 
 |  | ||||||
|         sat::literal false_literal; |  | ||||||
|         sat::literal true_literal; |  | ||||||
|         bool visit(expr* e) override; |         bool visit(expr* e) override; | ||||||
|         bool visited(expr* e) override; |         bool visited(expr* e) override; | ||||||
|         bool post_visit(expr* e, bool sign, bool root) override { return true; } |         bool post_visit(expr* e, bool sign, bool root) override; | ||||||
|         unsigned get_bv_size(euf::enode* n); |         unsigned get_bv_size(euf::enode* n); | ||||||
|         unsigned get_bv_size(theory_var v); |         unsigned get_bv_size(theory_var v); | ||||||
|         theory_var get_var(euf::enode* n); |         theory_var get_var(euf::enode* n); | ||||||
|  | @ -129,66 +182,55 @@ namespace bv { | ||||||
|         inline theory_var get_arg_var(euf::enode* n, unsigned idx); |         inline theory_var get_arg_var(euf::enode* n, unsigned idx); | ||||||
|         void get_bits(theory_var v, expr_ref_vector& r); |         void get_bits(theory_var v, expr_ref_vector& r); | ||||||
|         void get_bits(euf::enode* n, expr_ref_vector& r); |         void get_bits(euf::enode* n, expr_ref_vector& r); | ||||||
|         void get_arg_bits(euf::enode* n, unsigned idx, expr_ref_vector& r); |  | ||||||
|         void get_arg_bits(app* n, unsigned idx, expr_ref_vector& r); |         void get_arg_bits(app* n, unsigned idx, expr_ref_vector& r); | ||||||
|         euf::enode* mk_enode(expr* n, ptr_vector<euf::enode> const& args); |  | ||||||
|         void fixed_var_eh(theory_var v); |         void fixed_var_eh(theory_var v); | ||||||
| 
 |         bool is_bv(theory_var v) const { return bv.is_bv(var2expr(v)); } | ||||||
|         sat::status status() const { return sat::status::th(m_is_redundant, get_id());  } |         sat::status status() const { return sat::status::th(m_is_redundant, get_id());  } | ||||||
|         void add_unit(sat::literal lit); |  | ||||||
|         void register_true_false_bit(theory_var v, unsigned i); |         void register_true_false_bit(theory_var v, unsigned i); | ||||||
|         void add_bit(theory_var v, sat::literal lit); |         void add_bit(theory_var v, sat::literal lit); | ||||||
|         void init_bits(euf::enode * n, expr_ref_vector const & bits); |         void set_bit_eh(theory_var v, literal l, unsigned idx); | ||||||
|  |         void init_bits(expr* e, expr_ref_vector const & bits); | ||||||
|         void mk_bits(theory_var v); |         void mk_bits(theory_var v); | ||||||
|         expr_ref mk_bit2bool(expr* b, unsigned idx); |         void add_def(sat::literal def, sat::literal l); | ||||||
|  |         void internalize_unary(app* n, std::function<void(unsigned, expr* const*, expr_ref_vector&)>& fn); | ||||||
|  |         void internalize_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn); | ||||||
|  |         void internalize_ac_binary(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref_vector&)>& fn); | ||||||
|  |         void internalize_par_unary(app* n, std::function<void(unsigned, expr* const*, unsigned p, expr_ref_vector&)>& fn); | ||||||
|  |         void internalize_novfl(app* n, std::function<void(unsigned, expr* const*, expr* const*, expr_ref&)>& fn); | ||||||
|         void internalize_num(app * n, theory_var v);        |         void internalize_num(app * n, theory_var v);        | ||||||
|         void internalize_add(app * n); |  | ||||||
|         void internalize_sub(app * n); |  | ||||||
|         void internalize_mul(app * n); |  | ||||||
|         void internalize_udiv(app * n); |  | ||||||
|         void internalize_sdiv(app * n); |  | ||||||
|         void internalize_urem(app * n); |  | ||||||
|         void internalize_srem(app * n); |  | ||||||
|         void internalize_smod(app * n); |  | ||||||
|         void internalize_shl(app * n); |  | ||||||
|         void internalize_lshr(app * n); |  | ||||||
|         void internalize_ashr(app * n); |  | ||||||
|         void internalize_ext_rotate_left(app * n); |  | ||||||
|         void internalize_ext_rotate_right(app * n); |  | ||||||
|         void internalize_and(app * n); |  | ||||||
|         void internalize_or(app * n); |  | ||||||
|         void internalize_not(app * n); |  | ||||||
|         void internalize_nand(app * n); |  | ||||||
|         void internalize_nor(app * n); |  | ||||||
|         void internalize_xor(app * n); |  | ||||||
|         void internalize_xnor(app * n); |  | ||||||
|         void internalize_concat(app * n);         |         void internalize_concat(app * n);         | ||||||
|         void internalize_sign_extend(app * n); |  | ||||||
|         void internalize_zero_extend(app * n); |  | ||||||
|         void internalize_extract(app * n); |  | ||||||
|         void internalize_redand(app * n); |  | ||||||
|         void internalize_redor(app * n); |  | ||||||
|         void internalize_comp(app * n); |  | ||||||
|         void internalize_rotate_left(app * n); |  | ||||||
|         void internalize_rotate_right(app * n); |  | ||||||
|         void internalize_bv2int(app* n); |         void internalize_bv2int(app* n); | ||||||
|         void internalize_int2bv(app* n); |         void internalize_int2bv(app* n); | ||||||
|         void internalize_mkbv(app* n); |         void internalize_mkbv(app* n); | ||||||
|         void internalize_umul_no_overflow(app *n); |         void internalize_xor3(app* n); | ||||||
|         void internalize_smul_no_overflow(app *n); |         void internalize_carry(app* n); | ||||||
|         void internalize_smul_no_underflow(app *n); |         void internalize_sub(app* n); | ||||||
| 
 |         void internalize_extract(app* n); | ||||||
|  |         void internalize_bit2bool(app* n); | ||||||
|  |         template<bool Signed> | ||||||
|  |         void internalize_le(app* n); | ||||||
|         void assert_bv2int_axiom(app * n); |         void assert_bv2int_axiom(app * n); | ||||||
|         void assert_int2bv_axiom(app* n); |         void assert_int2bv_axiom(app* n); | ||||||
|  |         void assert_ackerman(theory_var v1, theory_var v2); | ||||||
| 
 | 
 | ||||||
|         // solving
 |         // solving
 | ||||||
|  |         theory_var find(theory_var v) const { return m_find.find(v); } | ||||||
|         void find_wpos(theory_var v); |         void find_wpos(theory_var v); | ||||||
|         void find_new_diseq_axioms(var_pos_occ* occs, theory_var v, unsigned idx); |         void find_new_diseq_axioms(bit_atom& a, theory_var v, unsigned idx); | ||||||
|         void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx); |         void mk_new_diseq_axiom(theory_var v1, theory_var v2, unsigned idx); | ||||||
|  |         bool get_fixed_value(theory_var v, numeral& result) const; | ||||||
|  |         void add_fixed_eq(theory_var v1, theory_var v2);       | ||||||
|  |         svector<theory_var>   m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits
 | ||||||
|  |         bool merge_zero_one_bits(theory_var r1, theory_var r2); | ||||||
|  |         void assign_bit(literal consequent, theory_var v1, theory_var v2, unsigned idx, literal antecedent, bool propagate_eqc); | ||||||
|  |         void propagate_bits(var_pos entry); | ||||||
|  |         numeral const& power2(unsigned i) const; | ||||||
| 
 | 
 | ||||||
|  |         // invariants
 | ||||||
|  |         bool check_zero_one_bits(theory_var v); | ||||||
|         |         | ||||||
|     public: |     public: | ||||||
|         solver(euf::solver& ctx); |         solver(euf::solver& ctx, theory_id id); | ||||||
|         ~solver() override {} |         ~solver() override {} | ||||||
|         void set_solver(sat::solver* s) override { m_solver = s; } |         void set_solver(sat::solver* s) override { m_solver = s; } | ||||||
|         void set_lookahead(sat::lookahead* s) override { } |         void set_lookahead(sat::lookahead* s) override { } | ||||||
|  | @ -197,19 +239,22 @@ namespace bv { | ||||||
|         bool is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) override; |         bool is_extended_binary(sat::ext_justification_idx idx, literal_vector& r) override; | ||||||
|         bool is_external(bool_var v) override; |         bool is_external(bool_var v) override; | ||||||
|         bool propagate(literal l, sat::ext_constraint_idx idx) override; |         bool propagate(literal l, sat::ext_constraint_idx idx) override; | ||||||
|         void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r) override; |         void get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) override; | ||||||
|         void asserted(literal l) override; |         void asserted(literal l) override; | ||||||
|         sat::check_result check() override; |         sat::check_result check() override; | ||||||
|         void push() override; |         void push() override; | ||||||
|         void pop(unsigned n) override; |         void pop(unsigned n) override; | ||||||
|         void pre_simplify() override;         |         void pre_simplify() override;         | ||||||
|         void simplify() override; |         void simplify() override; | ||||||
|  |         bool set_root(literal l, literal r) override; | ||||||
|  |         void flush_roots() override; | ||||||
|         void clauses_modifed() override; |         void clauses_modifed() override; | ||||||
|         lbool get_phase(bool_var v) override; |         lbool get_phase(bool_var v) override; | ||||||
|         std::ostream& display(std::ostream& out) const override; |         std::ostream& display(std::ostream& out) const override; | ||||||
|         std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override; |         std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override; | ||||||
|         std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override; |         std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override; | ||||||
|         void collect_statistics(statistics& st) const override; |         void collect_statistics(statistics& st) const override; | ||||||
|  |         euf::th_solver* fresh(sat::solver* s, euf::solver& ctx) override; | ||||||
|         extension* copy(sat::solver* s) override;        |         extension* copy(sat::solver* s) override;        | ||||||
|         void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {} |         void find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) override {} | ||||||
|         void gc() override {} |         void gc() override {} | ||||||
|  | @ -220,18 +265,31 @@ namespace bv { | ||||||
|         bool check_model(sat::model const& m) const override; |         bool check_model(sat::model const& m) const override; | ||||||
|         unsigned max_var(unsigned w) const override; |         unsigned max_var(unsigned w) const override; | ||||||
| 
 | 
 | ||||||
|  |         void new_eq_eh(euf::th_eq const& eq) override; | ||||||
|  |         bool unit_propagate() override; | ||||||
|  | 
 | ||||||
|  |         void add_value(euf::enode* n, expr_ref_vector& values) override; | ||||||
|  | 
 | ||||||
|         bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card, |         bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card, | ||||||
|                         std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override { return false; } |                         std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override { return false; } | ||||||
| 
 | 
 | ||||||
|         bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override { return false; } |         bool to_formulas(std::function<expr_ref(sat::literal)>& l2e, expr_ref_vector& fmls) override { return false; } | ||||||
|         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; |         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; | ||||||
|  |         void internalize(expr* e, bool redundant) override; | ||||||
|         euf::theory_var mk_var(euf::enode* n) override; |         euf::theory_var mk_var(euf::enode* n) override; | ||||||
|  |         void apply_sort_cnstr(euf::enode * n, sort * s) override; | ||||||
| 
 | 
 | ||||||
|          |          | ||||||
|  |         void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2); | ||||||
|  |         void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { SASSERT(check_zero_one_bits(r1)); } | ||||||
|  |         void unmerge_eh(theory_var v1, theory_var v2); | ||||||
|  |         trail_stack<euf::solver>& get_trail_stack(); | ||||||
|  | 
 | ||||||
|         // disagnostics
 |         // disagnostics
 | ||||||
|         std::ostream& display(std::ostream& out, theory_var v) const;         |         std::ostream& display(std::ostream& out, theory_var v) const;         | ||||||
|         typedef std::pair<solver const*, theory_var> pp_var; |         typedef std::pair<solver const*, theory_var> pp_var; | ||||||
|         pp_var pp(theory_var v) const { return pp_var(this, v); } |         pp_var pp(theory_var v) const { return pp_var(this, v); } | ||||||
|  | 
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     inline std::ostream& operator<<(std::ostream& out, solver::pp_var const& p) { return p.first->display(out, p.second); } |     inline std::ostream& operator<<(std::ostream& out, solver::pp_var const& p) { return p.first->display(out, p.second); } | ||||||
|  |  | ||||||
|  | @ -58,37 +58,10 @@ namespace euf { | ||||||
|         inf.b = b; |         inf.b = b; | ||||||
|         inf.c = nullptr; |         inf.c = nullptr; | ||||||
|         inf.is_cc = true; |         inf.is_cc = true; | ||||||
|  |         inf.m_count = 0; | ||||||
|         insert(); |         insert(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ackerman::remove_from_queue(inference* inf) { |  | ||||||
|         if (m_queue->m_next == m_queue) { |  | ||||||
|             SASSERT(inf == m_queue); |  | ||||||
|             m_queue = nullptr; |  | ||||||
|             return; |  | ||||||
|         }             |  | ||||||
|         if (m_queue == inf)  |  | ||||||
|             m_queue = inf->m_next; |  | ||||||
|         auto* next = inf->m_next; |  | ||||||
|         auto* prev = inf->m_prev; |  | ||||||
|         prev->m_next = next; |  | ||||||
|         next->m_prev = prev;         |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void ackerman::push_to_front(inference* inf) { |  | ||||||
|         if (!m_queue) { |  | ||||||
|             m_queue = inf; |  | ||||||
|         } |  | ||||||
|         else if (m_queue != inf) { |  | ||||||
|             auto* next = inf->m_next; |  | ||||||
|             auto* prev = inf->m_prev; |  | ||||||
|             prev->m_next = next; |  | ||||||
|             next->m_prev = prev; |  | ||||||
|             inf->m_prev = m_queue->m_prev; |  | ||||||
|             inf->m_next = m_queue; |  | ||||||
|             m_queue->m_prev = inf; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     void ackerman::insert() { |     void ackerman::insert() { | ||||||
|         inference* inf = m_tmp_inference; |         inference* inf = m_tmp_inference; | ||||||
|  | @ -97,15 +70,16 @@ namespace euf { | ||||||
|             m.inc_ref(inf->a); |             m.inc_ref(inf->a); | ||||||
|             m.inc_ref(inf->b); |             m.inc_ref(inf->b); | ||||||
|             m.inc_ref(inf->c); |             m.inc_ref(inf->c); | ||||||
|         } |  | ||||||
|         else  |  | ||||||
|             new_tmp();         |             new_tmp();         | ||||||
|  |         } | ||||||
|         other->m_count++; |         other->m_count++; | ||||||
|         push_to_front(other); |         if (other->m_count > m_high_watermark)  | ||||||
|  |             s.s().set_should_simplify(); | ||||||
|  |         inference::push_to_front(m_queue, other); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ackerman::remove(inference* inf) { |     void ackerman::remove(inference* inf) { | ||||||
|         remove_from_queue(inf); |         inference::remove_from(m_queue, inf); | ||||||
|         m_table.erase(inf); |         m_table.erase(inf); | ||||||
|         m.dec_ref(inf->a); |         m.dec_ref(inf->a); | ||||||
|         m.dec_ref(inf->b); |         m.dec_ref(inf->b); | ||||||
|  | @ -115,13 +89,10 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|     void ackerman::new_tmp() { |     void ackerman::new_tmp() { | ||||||
|         m_tmp_inference = alloc(inference); |         m_tmp_inference = alloc(inference); | ||||||
|         m_tmp_inference->m_next = m_tmp_inference->m_prev = m_tmp_inference; |         m_tmp_inference->init(m_tmp_inference); | ||||||
|         m_tmp_inference->m_count = 0; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ackerman::cg_conflict_eh(expr * n1, expr * n2) { |     void ackerman::cg_conflict_eh(expr * n1, expr * n2) { | ||||||
|         if (s.m_config.m_dack != DACK_ROOT) |  | ||||||
|             return; |  | ||||||
|         if (!is_app(n1) || !is_app(n2)) |         if (!is_app(n1) || !is_app(n2)) | ||||||
|             return; |             return; | ||||||
|         app* a = to_app(n1); |         app* a = to_app(n1); | ||||||
|  | @ -133,8 +104,6 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void ackerman::used_eq_eh(expr* a, expr* b, expr* c) { |     void ackerman::used_eq_eh(expr* a, expr* b, expr* c) { | ||||||
|         if (!s.m_config.m_dack_eq) |  | ||||||
|             return; |  | ||||||
|         if (a == b || a == c || b == c) |         if (a == b || a == c || b == c) | ||||||
|             return; |             return; | ||||||
|         insert(a, b, c); |         insert(a, b, c); | ||||||
|  | @ -142,8 +111,6 @@ namespace euf { | ||||||
|     } |     } | ||||||
|          |          | ||||||
|     void ackerman::used_cc_eh(app* a, app* b) { |     void ackerman::used_cc_eh(app* a, app* b) { | ||||||
|         if (s.m_config.m_dack != DACK_CR) |  | ||||||
|             return; |  | ||||||
|         SASSERT(a->get_decl() == b->get_decl()); |         SASSERT(a->get_decl() == b->get_decl()); | ||||||
|         SASSERT(a->get_num_args() == b->get_num_args());         |         SASSERT(a->get_num_args() == b->get_num_args());         | ||||||
|         insert(a, b); |         insert(a, b); | ||||||
|  | @ -157,7 +124,7 @@ namespace euf { | ||||||
|         m_num_propagations_since_last_gc = 0; |         m_num_propagations_since_last_gc = 0; | ||||||
|          |          | ||||||
|         while (m_table.size() > m_gc_threshold)  |         while (m_table.size() > m_gc_threshold)  | ||||||
|             remove(m_queue->m_prev);      |             remove(m_queue->prev()); | ||||||
|      |      | ||||||
|         m_gc_threshold *= 110; |         m_gc_threshold *= 110; | ||||||
|         m_gc_threshold /= 100; |         m_gc_threshold /= 100; | ||||||
|  | @ -171,13 +138,16 @@ namespace euf { | ||||||
|         unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.m_config.m_dack_factor); |         unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.m_config.m_dack_factor); | ||||||
|         num_prop = std::min(num_prop, m_table.size()); |         num_prop = std::min(num_prop, m_table.size()); | ||||||
|         for (unsigned i = 0; i < num_prop; ++i, n = k) { |         for (unsigned i = 0; i < num_prop; ++i, n = k) { | ||||||
|             k = n->m_next; |             k = n->next(); | ||||||
|             if (n->m_count < s.m_config.m_dack_threshold)  |             if (n->m_count < s.m_config.m_dack_threshold)  | ||||||
|                 continue; |                 continue; | ||||||
|  |             if (n->m_count >= m_high_watermark && num_prop < m_table.size()) | ||||||
|  |                 ++num_prop; | ||||||
|             if (n->is_cc)  |             if (n->is_cc)  | ||||||
|                 add_cc(n->a, n->b); |                 add_cc(n->a, n->b); | ||||||
|             else  |             else  | ||||||
|                 add_eq(n->a, n->b, n->c);        |                 add_eq(n->a, n->b, n->c);        | ||||||
|  |             ++s.m_stats.m_ackerman; | ||||||
|             remove(n); |             remove(n); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ Author: | ||||||
| --*/ | --*/ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include "util/dlist.h" | ||||||
| #include "ast/euf/euf_egraph.h" | #include "ast/euf/euf_egraph.h" | ||||||
| #include "sat/smt/atom2bool_var.h" | #include "sat/smt/atom2bool_var.h" | ||||||
| #include "sat/smt/sat_th.h" | #include "sat/smt/sat_th.h" | ||||||
|  | @ -24,13 +25,12 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|     class solver; |     class solver; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     class ackerman { |     class ackerman { | ||||||
| 
 | 
 | ||||||
|         struct inference { |         struct inference : dll_base<inference>{ | ||||||
|             bool is_cc; |             bool is_cc; | ||||||
|             expr* a, *b, *c; |             expr* a, *b, *c; | ||||||
|             inference* m_next{ nullptr }; |  | ||||||
|             inference* m_prev{ nullptr }; |  | ||||||
|             unsigned   m_count{ 0 }; |             unsigned   m_count{ 0 }; | ||||||
|             inference():is_cc(false), a(nullptr), b(nullptr), c(nullptr) {} |             inference():is_cc(false), a(nullptr), b(nullptr), c(nullptr) {} | ||||||
|             inference(app* a, app* b):is_cc(true), a(a), b(b), c(nullptr) {} |             inference(app* a, app* b):is_cc(true), a(a), b(b), c(nullptr) {} | ||||||
|  | @ -58,6 +58,7 @@ namespace euf { | ||||||
|         inference*    m_queue { nullptr }; |         inference*    m_queue { nullptr }; | ||||||
|         inference*    m_tmp_inference { nullptr }; |         inference*    m_tmp_inference { nullptr }; | ||||||
|         unsigned      m_gc_threshold { 1 }; |         unsigned      m_gc_threshold { 1 }; | ||||||
|  |         unsigned      m_high_watermark { 1000 }; | ||||||
|         unsigned      m_num_propagations_since_last_gc { 0 }; |         unsigned      m_num_propagations_since_last_gc { 0 }; | ||||||
|   |   | ||||||
|         void reset(); |         void reset(); | ||||||
|  | @ -69,8 +70,6 @@ namespace euf { | ||||||
|         void add_cc(expr* a, expr* b); |         void add_cc(expr* a, expr* b); | ||||||
|         void add_eq(expr* a, expr* b, expr* c);         |         void add_eq(expr* a, expr* b, expr* c);         | ||||||
|         void gc(); |         void gc(); | ||||||
|         void push_to_front(inference* inf);         |  | ||||||
|         void remove_from_queue(inference* inf); |  | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|         ackerman(solver& s, ast_manager& m); |         ackerman(solver& s, ast_manager& m); | ||||||
|  |  | ||||||
|  | @ -21,17 +21,27 @@ Author: | ||||||
| 
 | 
 | ||||||
| namespace euf { | namespace euf { | ||||||
| 
 | 
 | ||||||
|  |     void solver::internalize(expr* e, bool redundant) { | ||||||
|  |         if (si.is_bool_op(e)) | ||||||
|  |             attach_lit(si.internalize(e, redundant), e);         | ||||||
|  |         else if (auto* ext = get_solver(e)) | ||||||
|  |             ext->internalize(e, redundant); | ||||||
|  |         else | ||||||
|  |             visit_rec(m, e, false, false, redundant); | ||||||
|  |         SASSERT(m_egraph.find(e)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { |     sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { | ||||||
|         if (si.is_bool_op(e))  |         if (si.is_bool_op(e))  | ||||||
|             return si.internalize(e, redundant); |             return attach_lit(si.internalize(e, redundant), e);         | ||||||
|         auto* ext = get_solver(e); |         if (auto* ext = get_solver(e)) | ||||||
|         if (ext) |  | ||||||
|             return ext->internalize(e, sign, root, redundant); |             return ext->internalize(e, sign, root, redundant); | ||||||
| 
 |  | ||||||
|         if (!visit_rec(m, e, sign, root, redundant)) |         if (!visit_rec(m, e, sign, root, redundant)) | ||||||
|             return sat::null_literal; |             return sat::null_literal; | ||||||
|         SASSERT(m_egraph.find(e)); |         SASSERT(m_egraph.find(e)); | ||||||
|         return literal(m_expr2var.to_bool_var(e), sign); |         if (m.is_bool(e)) | ||||||
|  |             return literal(si.to_bool_var(e), sign); | ||||||
|  |         return sat::null_literal; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::visit(expr* e) { |     bool solver::visit(expr* e) { | ||||||
|  | @ -39,15 +49,7 @@ namespace euf { | ||||||
|         if (n) |         if (n) | ||||||
|             return true; |             return true; | ||||||
|         if (si.is_bool_op(e)) { |         if (si.is_bool_op(e)) { | ||||||
|             sat::literal lit = si.internalize(e, m_is_redundant); |             attach_lit(si.internalize(e, m_is_redundant), e); | ||||||
|             n = m_var2node.get(lit.var(), nullptr); |  | ||||||
|             if (n && !lit.sign()) |  | ||||||
|                 return n; |  | ||||||
|              |  | ||||||
|             n = m_egraph.mk(e, 0, nullptr); |  | ||||||
|             attach_lit(lit, n); |  | ||||||
|             if (!m.is_true(e) && !m.is_false(e))  |  | ||||||
|                 s().set_external(lit.var()); |  | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|         if (is_app(e) && to_app(e)->get_num_args() > 0) { |         if (is_app(e) && to_app(e)->get_num_args() > 0) { | ||||||
|  | @ -64,8 +66,12 @@ namespace euf { | ||||||
|         m_args.reset(); |         m_args.reset(); | ||||||
|         for (unsigned i = 0; i < num; ++i) |         for (unsigned i = 0; i < num; ++i) | ||||||
|             m_args.push_back(m_egraph.find(to_app(e)->get_arg(i))); |             m_args.push_back(m_egraph.find(to_app(e)->get_arg(i))); | ||||||
|         if (root && internalize_root(to_app(e), sign)) |         if (root && internalize_root(to_app(e), sign, m_args)) | ||||||
|             return false; |             return false; | ||||||
|  |         if (auto* s = get_solver(e)) { | ||||||
|  |             s->internalize(e, m_is_redundant); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|         enode* n = m_egraph.mk(e, num, m_args.c_ptr()); |         enode* n = m_egraph.mk(e, num, m_args.c_ptr()); | ||||||
|         attach_node(n); |         attach_node(n); | ||||||
|         return true; |         return true; | ||||||
|  | @ -77,33 +83,45 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|     void solver::attach_node(euf::enode* n) { |     void solver::attach_node(euf::enode* n) { | ||||||
|         expr* e = n->get_owner(); |         expr* e = n->get_owner(); | ||||||
|         log_node(n); |         if (!m.is_bool(e)) | ||||||
|         if (m.is_bool(e)) { |             log_node(e); | ||||||
|             sat::bool_var v = si.add_bool_var(e); |         else  | ||||||
|             log_bool_var(v, n); |             attach_lit(literal(si.add_bool_var(e), false),  e);         | ||||||
|             attach_lit(literal(v, false),  n); | 
 | ||||||
|  |         if (!m.is_bool(e) && m.get_sort(e)->get_family_id() != null_family_id) { | ||||||
|  |             auto* e_ext = get_solver(e); | ||||||
|  |             auto* s_ext = get_solver(m.get_sort(e)); | ||||||
|  |             if (s_ext && s_ext != e_ext) | ||||||
|  |                 s_ext->apply_sort_cnstr(n, m.get_sort(e)); | ||||||
|         } |         } | ||||||
|         axiomatize_basic(n); |         axiomatize_basic(n); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::attach_lit(literal lit, euf::enode* n) { |     sat::literal solver::attach_lit(literal lit, expr* e) { | ||||||
|         if (lit.sign()) { |         if (lit.sign()) { | ||||||
|             sat::bool_var v = si.add_bool_var(n->get_owner()); |             sat::bool_var v = si.add_bool_var(e); | ||||||
|  |             s().set_external(v); | ||||||
|             sat::literal lit2 = literal(v, false); |             sat::literal lit2 = literal(v, false); | ||||||
|             s().mk_clause(~lit, lit2, sat::status::th(false, m.get_basic_family_id())); |             s().mk_clause(~lit, lit2, sat::status::asserted()); | ||||||
|             s().mk_clause(lit, ~lit2, sat::status::th(false, m.get_basic_family_id())); |             s().mk_clause(lit, ~lit2, sat::status::asserted()); | ||||||
|             lit = lit2; |             lit = lit2; | ||||||
|         } |         } | ||||||
|         sat::bool_var v = lit.var(); |         sat::bool_var v = lit.var(); | ||||||
|         m_var2node.reserve(v + 1, nullptr); |         m_var2expr.reserve(v + 1, nullptr); | ||||||
|         SASSERT(m_var2node[v] == nullptr); |         SASSERT(m_var2expr[v] == nullptr); | ||||||
|         m_var2node[v] = n; |         m_var2expr[v] = e; | ||||||
|         m_var_trail.push_back(v); |         m_var_trail.push_back(v); | ||||||
|  |         s().set_external(v); | ||||||
|  |         if (!m_egraph.find(e)) { | ||||||
|  |             enode* n = m_egraph.mk(e, 0, nullptr); | ||||||
|  |             m_egraph.set_merge_enabled(n, false); | ||||||
|  |         } | ||||||
|  |         return lit; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::internalize_root(app* e, bool sign) { |     bool solver::internalize_root(app* e, bool sign, enode_vector const& args) { | ||||||
|         if (m.is_distinct(e)) { |         if (m.is_distinct(e)) { | ||||||
|             enode_vector _args(m_args); |             enode_vector _args(args); | ||||||
|             if (sign) |             if (sign) | ||||||
|                 add_not_distinct_axiom(e, _args.c_ptr()); |                 add_not_distinct_axiom(e, _args.c_ptr()); | ||||||
|             else |             else | ||||||
|  | @ -202,7 +220,7 @@ namespace euf { | ||||||
|             expr* c = a->get_arg(0); |             expr* c = a->get_arg(0); | ||||||
|             expr* th = a->get_arg(1); |             expr* th = a->get_arg(1); | ||||||
|             expr* el = a->get_arg(2); |             expr* el = a->get_arg(2); | ||||||
|             sat::bool_var v = m_expr2var.to_bool_var(c); |             sat::bool_var v = si.to_bool_var(c); | ||||||
|             SASSERT(v != sat::null_bool_var); |             SASSERT(v != sat::null_bool_var); | ||||||
|             SASSERT(!m.is_bool(e)); |             SASSERT(!m.is_bool(e)); | ||||||
|             expr_ref eq_th(m.mk_eq(a, th), m); |             expr_ref eq_th(m.mk_eq(a, th), m); | ||||||
|  | @ -224,7 +242,7 @@ namespace euf { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             expr_ref fml(m.mk_or(eqs), m); |             expr_ref fml(m.mk_or(eqs), m); | ||||||
|             sat::literal dist(m_expr2var.to_bool_var(e), false); |             sat::literal dist(si.to_bool_var(e), false); | ||||||
|             sat::literal some_eq = si.internalize(fml, m_is_redundant); |             sat::literal some_eq = si.internalize(fml, m_is_redundant); | ||||||
|             sat::literal lits1[2] = { ~dist, ~some_eq }; |             sat::literal lits1[2] = { ~dist, ~some_eq }; | ||||||
|             sat::literal lits2[2] = { dist, some_eq }; |             sat::literal lits2[2] = { dist, some_eq }; | ||||||
|  |  | ||||||
							
								
								
									
										48
									
								
								src/sat/smt/euf_invariant.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/sat/smt/euf_invariant.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2020 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     euf_invariant.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Check invariants for EUF solver. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2020-09-07 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "sat/smt/euf_solver.h" | ||||||
|  | 
 | ||||||
|  | namespace euf { | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        \brief Check if expressions attached to bool_variables and enodes have a consistent assignment. | ||||||
|  |        For all a, b.  root(a) == root(b) ==> get_assignment(a) == get_assignment(b) | ||||||
|  |     */ | ||||||
|  | 
 | ||||||
|  |     void solver::check_eqc_bool_assignment() const { | ||||||
|  |         for (enode* n : m_egraph.nodes()) { | ||||||
|  |             VERIFY(!m.is_bool(n->get_owner()) ||  | ||||||
|  |                    s().value(get_literal(n)) == s().value(get_literal(n->get_root())));         | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::check_missing_bool_enode_propagation() const { | ||||||
|  |         for (enode* n : m_egraph.nodes())  | ||||||
|  |             if (m.is_bool(n->get_owner()) && l_undef == s().value(get_literal(n))) { | ||||||
|  |                 if (!n->is_root()) { | ||||||
|  |                     VERIFY(l_undef == s().value(get_literal(n->get_root()))); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                     for (enode* o : enode_class(n)) { | ||||||
|  |                         VERIFY(l_undef == s().value(get_literal(o))); | ||||||
|  |                     } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -16,6 +16,7 @@ Author: | ||||||
| --*/ | --*/ | ||||||
| 
 | 
 | ||||||
| #include "ast/ast_pp.h" | #include "ast/ast_pp.h" | ||||||
|  | #include "ast/ast_ll_pp.h" | ||||||
| #include "sat/smt/euf_solver.h" | #include "sat/smt/euf_solver.h" | ||||||
| #include "model/value_factory.h" | #include "model/value_factory.h" | ||||||
| 
 | 
 | ||||||
|  | @ -79,7 +80,7 @@ namespace euf { | ||||||
|                 } |                 } | ||||||
|                 if (is_app(e) && to_app(e)->get_family_id() == m.get_basic_family_id()) |                 if (is_app(e) && to_app(e)->get_family_id() == m.get_basic_family_id()) | ||||||
|                     continue; |                     continue; | ||||||
|                 sat::bool_var v = m_expr2var.to_bool_var(e); |                 sat::bool_var v = si.to_bool_var(e); | ||||||
|                 SASSERT(v != sat::null_bool_var); |                 SASSERT(v != sat::null_bool_var); | ||||||
|                 switch (s().value(v)) { |                 switch (s().value(v)) { | ||||||
|                 case l_true: |                 case l_true: | ||||||
|  | @ -100,6 +101,8 @@ namespace euf { | ||||||
|                 expr* v = user_sort.get_fresh_value(m.get_sort(e)); |                 expr* v = user_sort.get_fresh_value(m.get_sort(e)); | ||||||
|                 values.set(id, v); |                 values.set(id, v); | ||||||
|             } |             } | ||||||
|  |             else if ((mb = get_solver(m.get_sort(e)))) | ||||||
|  |                 mb->add_value(n, values); | ||||||
|             else { |             else { | ||||||
|                 IF_VERBOSE(1, verbose_stream() << "no model values created for " << mk_pp(e, m) << "\n"); |                 IF_VERBOSE(1, verbose_stream() << "no model values created for " << mk_pp(e, m) << "\n"); | ||||||
|             }                 |             }                 | ||||||
|  | @ -119,6 +122,9 @@ namespace euf { | ||||||
|             if (m.is_bool(e) && is_uninterp_const(e) && mdl->get_const_interp(f)) |             if (m.is_bool(e) && is_uninterp_const(e) && mdl->get_const_interp(f)) | ||||||
|                 continue; |                 continue; | ||||||
|             expr* v = values.get(n->get_root_id()); |             expr* v = values.get(n->get_root_id()); | ||||||
|  |             CTRACE("euf", !v, tout << "no value for " << mk_pp(e, m) << "\n";); | ||||||
|  |             if (!v) | ||||||
|  |                 continue; | ||||||
|             unsigned arity = f->get_arity(); |             unsigned arity = f->get_arity(); | ||||||
|             if (arity == 0)  |             if (arity == 0)  | ||||||
|                 mdl->register_decl(f, v); |                 mdl->register_decl(f, v); | ||||||
|  | @ -129,8 +135,11 @@ namespace euf { | ||||||
|                     mdl->register_decl(f, fi); |                     mdl->register_decl(f, fi); | ||||||
|                 } |                 } | ||||||
|                 args.reset(); |                 args.reset(); | ||||||
|                 for (enode* arg : enode_args(n))  |                 for (enode* arg : enode_args(n)) { | ||||||
|                     args.push_back(values.get(arg->get_root_id())); |                     args.push_back(values.get(arg->get_root_id())); | ||||||
|  |                     SASSERT(args.back()); | ||||||
|  |                 } | ||||||
|  |                 SASSERT(args.size() == arity); | ||||||
|                 if (!fi->get_entry(args.c_ptr())) |                 if (!fi->get_entry(args.c_ptr())) | ||||||
|                     fi->insert_new_entry(args.c_ptr(), v); |                     fi->insert_new_entry(args.c_ptr(), v); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -19,53 +19,60 @@ Author: | ||||||
| 
 | 
 | ||||||
| namespace euf { | namespace euf { | ||||||
| 
 | 
 | ||||||
|     void solver::log_node(enode* n) { |     void solver::init_drat() { | ||||||
|         if (m_drat) { |         if (!m_drat_initialized) | ||||||
|             expr* e = n->get_owner(); |             get_drat().add_theory(m.get_basic_family_id(), symbol("euf")); | ||||||
|             if (is_app(e)) { |         m_drat_initialized = true; | ||||||
|                 symbol const& name = to_app(e)->get_name(); |  | ||||||
|                 s().get_drat().def_begin(n->get_owner_id(), name); |  | ||||||
|                 for (enode* arg : enode_args(n)) { |  | ||||||
|                     s().get_drat().def_add_arg(arg->get_owner_id()); |  | ||||||
|     } |     } | ||||||
|                 s().get_drat().def_end(); | 
 | ||||||
|  |     void solver::log_node(expr* e) { | ||||||
|  |         if (!use_drat()) | ||||||
|  |             return; | ||||||
|  |         if (is_app(e)) { | ||||||
|  |             std::stringstream strm; | ||||||
|  |             strm << mk_ismt2_func(to_app(e)->get_decl(), m); | ||||||
|  |             get_drat().def_begin(e->get_id(), strm.str()); | ||||||
|  |             for (expr* arg : *to_app(e)) | ||||||
|  |                 get_drat().def_add_arg(arg->get_id()); | ||||||
|  |             get_drat().def_end(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             IF_VERBOSE(0, verbose_stream() << "logging binders is TBD\n"); |             IF_VERBOSE(0, verbose_stream() << "logging binders is TBD\n"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void solver::log_bool_var(sat::bool_var v, enode* n) { |  | ||||||
|         if (m_drat) { |  | ||||||
|             s().get_drat().bool_def(v, n->get_owner_id()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * \brief logs antecedents to a proof trail. | ||||||
|  |      * | ||||||
|  |      * NB with theories, this is not a pure EUF justification, | ||||||
|  |      * It is true modulo EUF and previously logged certificates | ||||||
|  |      * so it isn't necessarily an axiom over EUF, | ||||||
|  |      * We will here leave it to the EUF checker to perform resolution steps. | ||||||
|  |      */ | ||||||
|     void solver::log_antecedents(literal l, literal_vector const& r) { |     void solver::log_antecedents(literal l, literal_vector const& r) { | ||||||
|         TRACE("euf", log_antecedents(tout, l, r);); |         TRACE("euf", log_antecedents(tout, l, r);); | ||||||
|         if (m_drat) { |         if (!use_drat()) | ||||||
|  |             return; | ||||||
|         literal_vector lits; |         literal_vector lits; | ||||||
|         for (literal lit : r) lits.push_back(~lit); |         for (literal lit : r) lits.push_back(~lit); | ||||||
|         if (l != sat::null_literal) |         if (l != sat::null_literal) | ||||||
|             lits.push_back(l); |             lits.push_back(l); | ||||||
|             s().get_drat().add(lits, sat::status::th(true, m.get_basic_family_id())); |         get_drat().add(lits, sat::status::th(true, m.get_basic_family_id())); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) { |     void solver::log_antecedents(std::ostream& out, literal l, literal_vector const& r) { | ||||||
|         for (sat::literal l : r) { |         for (sat::literal l : r) { | ||||||
|             enode* n = m_var2node[l.var()]; |             expr* n = m_var2expr[l.var()]; | ||||||
|             out << ~l << ": "; |             out << ~l << ": "; | ||||||
|             if (!l.sign()) out << "! "; |             if (!l.sign()) out << "! "; | ||||||
|             out << mk_pp(n->get_owner(), m) << "\n"; |             out << mk_pp(n, m) << "\n"; | ||||||
|             SASSERT(s().value(l) == l_true); |             SASSERT(s().value(l) == l_true); | ||||||
|         } |         } | ||||||
|         if (l != sat::null_literal) { |         if (l != sat::null_literal) { | ||||||
|             out << l << ": "; |             out << l << ": "; | ||||||
|             if (l.sign()) out << "! "; |             if (l.sign()) out << "! "; | ||||||
|             enode* n = m_var2node[l.var()]; |             expr* n = m_var2expr[l.var()]; | ||||||
|             out << mk_pp(n->get_owner(), m) << "\n";             |             out << mk_pp(n, m) << "\n"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,27 +20,25 @@ Author: | ||||||
| #include "sat/sat_solver.h" | #include "sat/sat_solver.h" | ||||||
| #include "sat/smt/sat_smt.h" | #include "sat/smt/sat_smt.h" | ||||||
| #include "sat/smt/ba_solver.h" | #include "sat/smt/ba_solver.h" | ||||||
|  | #include "sat/smt/bv_solver.h" | ||||||
| #include "sat/smt/euf_solver.h" | #include "sat/smt/euf_solver.h" | ||||||
| 
 | 
 | ||||||
| namespace euf { | namespace euf { | ||||||
| 
 | 
 | ||||||
|     void solver::updt_params(params_ref const& p) { |     void solver::updt_params(params_ref const& p) { | ||||||
|         m_config.updt_params(p); |         m_config.updt_params(p); | ||||||
|         m_drat = m_solver && m_solver->get_config().m_drat; |  | ||||||
|         if (m_drat)  |  | ||||||
|             m_solver->get_drat().add_theory(m.get_basic_family_id(), symbol("euf")); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /**
 |     /**
 | ||||||
|     * retrieve extension that is associated with Boolean variable. |     * retrieve extension that is associated with Boolean variable. | ||||||
|     */ |     */ | ||||||
|     th_solver* solver::get_solver(sat::bool_var v) { |     th_solver* solver::get_solver(sat::bool_var v) { | ||||||
|         if (v >= m_var2node.size()) |         if (v >= m_var2expr.size()) | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         euf::enode* n = m_var2node[v]; |         expr* e = m_var2expr[v]; | ||||||
|         if (!n) |         if (!e) | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         return get_solver(n->get_owner()); |         return get_solver(e); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     th_solver* solver::get_solver(expr* e) { |     th_solver* solver::get_solver(expr* e) { | ||||||
|  | @ -49,8 +47,7 @@ namespace euf { | ||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     th_solver* solver::get_solver(func_decl* f) { |     th_solver* solver::get_solver(family_id fid, func_decl* f) { | ||||||
|         family_id fid = f->get_family_id(); |  | ||||||
|         if (fid == null_family_id) |         if (fid == null_family_id) | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         auto* ext = m_id2solver.get(fid, nullptr); |         auto* ext = m_id2solver.get(fid, nullptr); | ||||||
|  | @ -59,19 +56,24 @@ namespace euf { | ||||||
|         if (fid == m.get_basic_family_id()) |         if (fid == m.get_basic_family_id()) | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         pb_util pb(m); |         pb_util pb(m); | ||||||
|  |         bv_util bvu(m); | ||||||
|         if (pb.get_family_id() == fid) { |         if (pb.get_family_id() == fid) { | ||||||
|             ext = alloc(sat::ba_solver, *this, fid); |             ext = alloc(sat::ba_solver, *this, fid); | ||||||
|             if (m_drat) |             if (use_drat()) | ||||||
|                 m_solver->get_drat().add_theory(fid, symbol("ba")); |                 s().get_drat().add_theory(fid, symbol("ba")); | ||||||
|  |         } | ||||||
|  |         else if (bvu.get_family_id() == fid) { | ||||||
|  |             ext = alloc(bv::solver, *this, fid); | ||||||
|  |             if (use_drat()) | ||||||
|  |                 s().get_drat().add_theory(fid, symbol("bv"));             | ||||||
|         } |         } | ||||||
|         if (ext) { |         if (ext) { | ||||||
|             ext->set_solver(m_solver); |             ext->set_solver(m_solver); | ||||||
|             ext->push_scopes(s().num_scopes()); |             ext->push_scopes(s().num_scopes()); | ||||||
|             add_solver(fid, ext); |             add_solver(fid, ext); | ||||||
|         } |         } | ||||||
|         else { |         else if (f)  | ||||||
|             unhandled_function(f); |             unhandled_function(f); | ||||||
|         } |  | ||||||
|         return ext; |         return ext; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -84,7 +86,7 @@ namespace euf { | ||||||
|         if (m_unhandled_functions.contains(f)) |         if (m_unhandled_functions.contains(f)) | ||||||
|             return; |             return; | ||||||
|         m_unhandled_functions.push_back(f); |         m_unhandled_functions.push_back(f); | ||||||
|         m_trail.push_back(new (m_region) push_back_vector<solver, func_decl_ref_vector>(m_unhandled_functions)); |         m_trail.push(push_back_vector<solver, func_decl_ref_vector>(m_unhandled_functions)); | ||||||
|         IF_VERBOSE(0, verbose_stream() << mk_pp(f, m) << " not handled\n"); |         IF_VERBOSE(0, verbose_stream() << mk_pp(f, m) << " not handled\n"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -93,7 +95,7 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::is_external(bool_var v) { |     bool solver::is_external(bool_var v) { | ||||||
|         if (nullptr != m_var2node.get(v, nullptr)) |         if (nullptr != m_var2expr.get(v, nullptr)) | ||||||
|             return true; |             return true; | ||||||
|         for (auto* s : m_solvers) |         for (auto* s : m_solvers) | ||||||
|             if (s->is_external(v)) |             if (s->is_external(v)) | ||||||
|  | @ -107,86 +109,125 @@ namespace euf { | ||||||
|         return ext->propagate(l, idx); |         return ext->propagate(l, idx); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r) { |     void solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) { | ||||||
|  |         m_egraph.begin_explain(); | ||||||
|  |         m_explain.reset(); | ||||||
|         auto* ext = sat::constraint_base::to_extension(idx); |         auto* ext = sat::constraint_base::to_extension(idx); | ||||||
|         if (ext == this) |         if (ext == this) | ||||||
|             get_antecedents(l, constraint::from_idx(idx), r); |             get_antecedents(l, constraint::from_idx(idx), r, probing); | ||||||
|         else |         else | ||||||
|             ext->get_antecedents(l, idx, r); |             ext->get_antecedents(l, idx, r, probing); | ||||||
|  |         for (unsigned qhead = 0; qhead < m_explain.size(); ++qhead) { | ||||||
|  |             size_t* e = m_explain[qhead]; | ||||||
|  |             if (is_literal(e)) | ||||||
|  |                 r.push_back(get_literal(e)); | ||||||
|  |             else { | ||||||
|  |                 size_t idx = get_justification(e); | ||||||
|  |                 auto* ext = sat::constraint_base::to_extension(idx); | ||||||
|  |                 SASSERT(ext != this); | ||||||
|  |                 sat::literal lit = sat::null_literal; | ||||||
|  |                 ext->get_antecedents(lit, idx, r, probing); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         m_egraph.end_explain(); | ||||||
|  |         if (!probing) | ||||||
|  |             log_antecedents(l, r); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::get_antecedents(literal l, constraint& j, literal_vector& r) { |     void solver::add_antecedent(enode* a, enode* b) { | ||||||
|         m_explain.reset(); |         m_egraph.explain_eq<size_t>(m_explain, a, b); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::propagate(enode* a, enode* b, ext_justification_idx idx) { | ||||||
|  |         m_egraph.merge(a, b, to_ptr(idx)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void solver::get_antecedents(literal l, constraint& j, literal_vector& r, bool probing) { | ||||||
|  |         expr* e = nullptr; | ||||||
|         euf::enode* n = nullptr; |         euf::enode* n = nullptr; | ||||||
| 
 | 
 | ||||||
|         // init_ackerman();
 |         init_ackerman(); | ||||||
| 
 | 
 | ||||||
|         switch (j.kind()) { |         switch (j.kind()) { | ||||||
|         case constraint::kind_t::conflict: |         case constraint::kind_t::conflict: | ||||||
|             SASSERT(m_egraph.inconsistent()); |             SASSERT(m_egraph.inconsistent()); | ||||||
|             m_egraph.explain<unsigned>(m_explain); |             m_egraph.explain<size_t>(m_explain); | ||||||
|             break; |             break; | ||||||
|         case constraint::kind_t::eq: |         case constraint::kind_t::eq: | ||||||
|             n = m_var2node[l.var()]; |             e = m_var2expr[l.var()]; | ||||||
|  |             n = m_egraph.find(e); | ||||||
|             SASSERT(n); |             SASSERT(n); | ||||||
|             SASSERT(m_egraph.is_equality(n)); |             SASSERT(m_egraph.is_equality(n)); | ||||||
|             SASSERT(!l.sign()); |             SASSERT(!l.sign()); | ||||||
|             m_egraph.explain_eq<unsigned>(m_explain, n->get_arg(0), n->get_arg(1)); |             m_egraph.explain_eq<size_t>(m_explain, n->get_arg(0), n->get_arg(1)); | ||||||
|             break; |             break; | ||||||
|         case constraint::kind_t::lit: |         case constraint::kind_t::lit: | ||||||
|             n = m_var2node[l.var()]; |             e = m_var2expr[l.var()]; | ||||||
|  |             n = m_egraph.find(e); | ||||||
|             SASSERT(n); |             SASSERT(n); | ||||||
|             SASSERT(m.is_bool(n->get_owner())); |             SASSERT(m.is_bool(n->get_owner())); | ||||||
|             m_egraph.explain_eq<unsigned>(m_explain, n, (l.sign() ? mk_false() : mk_true())); |             m_egraph.explain_eq<size_t>(m_explain, n, (l.sign() ? mk_false() : mk_true())); | ||||||
|             break; |             break; | ||||||
|         default: |         default: | ||||||
|             IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n"); |             IF_VERBOSE(0, verbose_stream() << (unsigned)j.kind() << "\n"); | ||||||
|             UNREACHABLE(); |             UNREACHABLE(); | ||||||
|         } |         } | ||||||
|         for (unsigned* idx : m_explain)  |  | ||||||
|             r.push_back(sat::to_literal((unsigned)(idx - base_ptr()))); |  | ||||||
| 
 |  | ||||||
|         log_antecedents(l, r); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::asserted(literal l) { |     void solver::asserted(literal l) { | ||||||
|         auto* ext = get_solver(l.var()); |         expr* e = m_var2expr.get(l.var(), nullptr); | ||||||
|         if (ext) { |         if (!e) { | ||||||
|             ext->asserted(l); |  | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         bool sign = l.sign();         |         bool sign = l.sign();         | ||||||
|         auto n = m_var2node.get(l.var(), nullptr); |         TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " " << (sign ? "not ": " ") << e->get_id()  << "\n";); | ||||||
|  |         euf::enode* n = m_egraph.find(e); | ||||||
|         if (!n) |         if (!n) | ||||||
|             return; |             return; | ||||||
|          |         for (auto th : enode_th_vars(n))            | ||||||
|         expr* e = n->get_owner(); |             m_id2solver[th.get_id()]->asserted(l); | ||||||
|         if (m.is_eq(e) && !sign) { |         if (!n->merge_enabled()) | ||||||
|  |             return; | ||||||
|  |         size_t* c = to_ptr(l); | ||||||
|  |         SASSERT(is_literal(c)); | ||||||
|  |         SASSERT(l == get_literal(c)); | ||||||
|  |         if (m.is_eq(e) && !sign && n->num_args() == 2) { | ||||||
|             euf::enode* na = n->get_arg(0); |             euf::enode* na = n->get_arg(0); | ||||||
|             euf::enode* nb = n->get_arg(1); |             euf::enode* nb = n->get_arg(1); | ||||||
|             TRACE("euf", tout << mk_pp(e, m) << "\n";); |             m_egraph.merge(na, nb, c); | ||||||
|             m_egraph.merge(na, nb, base_ptr() + l.index()); |  | ||||||
|         } |         } | ||||||
|         else {             |         else {             | ||||||
|             euf::enode* nb = sign ? mk_false() : mk_true(); |             euf::enode* nb = sign ? mk_false() : mk_true(); | ||||||
|             TRACE("euf", tout << (sign ? "not ": " ") << mk_pp(n->get_owner(), m)  << "\n";); |             m_egraph.merge(n, nb, c); | ||||||
|             m_egraph.merge(n, nb, base_ptr() + l.index()); |  | ||||||
|         } |         } | ||||||
|         // TBD: delay propagation?
 |  | ||||||
|         propagate(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::propagate() {   |     bool solver::unit_propagate() { | ||||||
|         while (m_egraph.propagate() && !s().inconsistent()) { |         bool propagated = false; | ||||||
|  |         while (!s().inconsistent()) { | ||||||
|             if (m_egraph.inconsistent()) {   |             if (m_egraph.inconsistent()) {   | ||||||
|                 unsigned lvl = s().scope_lvl(); |                 unsigned lvl = s().scope_lvl(); | ||||||
|                 s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index())); |                 s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index())); | ||||||
|                 return; |                 return true; | ||||||
|             } |             } | ||||||
|  |             bool propagated1 = false; | ||||||
|  |             if (m_egraph.propagate()) { | ||||||
|                 propagate_literals(); |                 propagate_literals(); | ||||||
|                 propagate_th_eqs(); |                 propagate_th_eqs(); | ||||||
|  |                 propagated1 = true; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             for (auto* s : m_solvers) { | ||||||
|  |                 if (s->unit_propagate()) | ||||||
|  |                     propagated1 = true; | ||||||
|  |             } | ||||||
|  |             if (!propagated1) | ||||||
|  |                 break; | ||||||
|  |             propagated = true;              | ||||||
|  |         } | ||||||
|  |         return propagated; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::propagate_literals() { |     void solver::propagate_literals() { | ||||||
|  | @ -196,7 +237,7 @@ namespace euf { | ||||||
|             bool is_eq = p.second; |             bool is_eq = p.second; | ||||||
|             expr* e = n->get_owner(); |             expr* e = n->get_owner(); | ||||||
|             expr* a = nullptr, *b = nullptr; |             expr* a = nullptr, *b = nullptr; | ||||||
|             bool_var v = m_expr2var.to_bool_var(e); |             bool_var v = si.to_bool_var(e); | ||||||
|             SASSERT(m.is_bool(e)); |             SASSERT(m.is_bool(e)); | ||||||
|             size_t cnstr; |             size_t cnstr; | ||||||
|             literal lit; |             literal lit; | ||||||
|  | @ -207,7 +248,7 @@ namespace euf { | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 a = e, b = n->get_root()->get_owner(); |                 a = e, b = n->get_root()->get_owner(); | ||||||
|                 SASSERT(m.is_true(a) || m.is_false(b)); |                 SASSERT(m.is_true(b) || m.is_false(b)); | ||||||
|                 cnstr = lit_constraint().to_index(); |                 cnstr = lit_constraint().to_index(); | ||||||
|                 lit = literal(v, m.is_false(b)); |                 lit = literal(v, m.is_false(b)); | ||||||
|             } |             } | ||||||
|  | @ -246,6 +287,7 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     sat::check_result solver::check() {  |     sat::check_result solver::check() {  | ||||||
|  |         TRACE("euf", s().display(tout);); | ||||||
|         bool give_up = false; |         bool give_up = false; | ||||||
|         bool cont = false; |         bool cont = false; | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|  | @ -262,37 +304,52 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::push() { |     void solver::push() { | ||||||
|  |         si.push(); | ||||||
|         scope s; |         scope s; | ||||||
|         s.m_var_lim = m_var_trail.size(); |         s.m_var_lim = m_var_trail.size(); | ||||||
|         s.m_trail_lim = m_trail.size(); |  | ||||||
|         m_scopes.push_back(s); |         m_scopes.push_back(s); | ||||||
|         m_region.push_scope(); |         m_trail.push_scope(); | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|             e->push(); |             e->push(); | ||||||
|         m_egraph.push(); |         m_egraph.push(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::force_push() { |  | ||||||
|         for (; m_num_scopes > 0; --m_num_scopes) { |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void solver::pop(unsigned n) { |     void solver::pop(unsigned n) { | ||||||
|         m_egraph.pop(n); |         m_egraph.pop(n); | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|             e->pop(n); |             e->pop(n); | ||||||
| 
 |  | ||||||
|         scope const & s = m_scopes[m_scopes.size() - n]; |         scope const & s = m_scopes[m_scopes.size() - n]; | ||||||
| 
 |  | ||||||
|         for (unsigned i = m_var_trail.size(); i-- > s.m_var_lim; ) |         for (unsigned i = m_var_trail.size(); i-- > s.m_var_lim; ) | ||||||
|             m_var2node[m_var_trail[i]] = nullptr; |             m_var2expr[m_var_trail[i]] = nullptr; | ||||||
|         m_var_trail.shrink(s.m_var_lim);         |         m_var_trail.shrink(s.m_var_lim);         | ||||||
|          |         m_trail.pop_scope(n); | ||||||
|         undo_trail_stack(*this, m_trail, s.m_trail_lim); |  | ||||||
| 
 |  | ||||||
|         m_region.pop_scope(n); |  | ||||||
|         m_scopes.shrink(m_scopes.size() - n); |         m_scopes.shrink(m_scopes.size() - n); | ||||||
|  |         si.pop(n); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::start_reinit(unsigned n) { | ||||||
|  |         sat::literal_vector lits; | ||||||
|  |         m_reinit_vars.reset(); | ||||||
|  |         m_reinit_exprs.reset(); | ||||||
|  |         s().get_reinit_literals(n, lits); | ||||||
|  |         for (sat::literal lit : lits) { | ||||||
|  |             sat::bool_var v = lit.var(); | ||||||
|  |             expr* e = bool_var2expr(v); | ||||||
|  |             if (m_reinit_vars.contains(v) || !e) | ||||||
|  |                 continue; | ||||||
|  |             m_reinit_vars.push_back(v); | ||||||
|  |             m_reinit_exprs.push_back(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::finish_reinit() { | ||||||
|  |         unsigned sz = m_reinit_vars.size(); | ||||||
|  |         for (unsigned i = 0; i < sz; ++i) { | ||||||
|  |             euf::enode* n = get_enode(m_reinit_exprs.get(i)); | ||||||
|  |             if (n) | ||||||
|  |                 continue; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::pre_simplify() { |     void solver::pre_simplify() { | ||||||
|  | @ -319,13 +376,33 @@ namespace euf { | ||||||
|         return l_undef;  |         return l_undef;  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     bool solver::set_root(literal l, literal r) { | ||||||
|  |         bool ok = true; | ||||||
|  |         for (auto* s : m_solvers) | ||||||
|  |             if (!s->set_root(l, r)) | ||||||
|  |                 ok = false; | ||||||
|  |         expr* e = bool_var2expr(l.var()); | ||||||
|  |         if (e) { | ||||||
|  |             if (m.is_eq(e) && !m.is_iff(e)) | ||||||
|  |                 ok = false; | ||||||
|  |             euf::enode* n = get_enode(e); | ||||||
|  |             if (n && n->merge_enabled()) | ||||||
|  |                 ok = false; | ||||||
|  |         } | ||||||
|  |         return ok; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void solver::flush_roots() { | ||||||
|  |         for (auto* s : m_solvers) | ||||||
|  |             s->flush_roots(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     std::ostream& solver::display(std::ostream& out) const { |     std::ostream& solver::display(std::ostream& out) const { | ||||||
|         m_egraph.display(out); |         m_egraph.display(out); | ||||||
| 
 |  | ||||||
|         out << "bool-vars\n"; |         out << "bool-vars\n"; | ||||||
|         for (unsigned v : m_var_trail) { |         for (unsigned v : m_var_trail) { | ||||||
|             euf::enode* n = m_var2node[v]; |             expr* e = m_var2expr[v]; | ||||||
|             out << v << ": " << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), m, 1) << "\n";         |             out << v << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";         | ||||||
|         } |         } | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|             e->display(out); |             e->display(out); | ||||||
|  | @ -350,13 +427,19 @@ namespace euf { | ||||||
|         m_egraph.collect_statistics(st); |         m_egraph.collect_statistics(st); | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|             e->collect_statistics(st); |             e->collect_statistics(st); | ||||||
|         st.update("euf dynack", m_stats.m_num_dynack); |         st.update("euf ackerman", m_stats.m_ackerman); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     sat::extension* solver::copy(sat::solver* s) {  |     sat::extension* solver::copy(sat::solver* s) {  | ||||||
|         auto* r = alloc(solver, *m_to_m, *m_to_expr2var, *m_to_si); |         auto* r = alloc(solver, *m_to_m, *m_to_si); | ||||||
|         r->m_config = m_config; |         r->m_config = m_config; | ||||||
|         std::function<void* (void*)> copy_justification = [&](void* x) { return (void*)(r->base_ptr() + ((unsigned*)x - base_ptr())); }; |         sat::literal true_lit = sat::null_literal; | ||||||
|  |         if (s->init_trail_size() > 0) | ||||||
|  |             true_lit = s->trail_literal(0);  | ||||||
|  |         std::function<void* (void*)> copy_justification = [&](void* x) {  | ||||||
|  |             SASSERT(true_lit != sat::null_literal);  | ||||||
|  |             return (void*)(r->to_ptr(true_lit));  | ||||||
|  |         }; | ||||||
|         r->m_egraph.copy_from(m_egraph, copy_justification);         |         r->m_egraph.copy_from(m_egraph, copy_justification);         | ||||||
|         r->set_solver(s); |         r->set_solver(s); | ||||||
|         for (unsigned i = 0; i < m_id2solver.size(); ++i) { |         for (unsigned i = 0; i < m_id2solver.size(); ++i) { | ||||||
|  | @ -386,6 +469,9 @@ namespace euf { | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|             if (!e->validate()) |             if (!e->validate()) | ||||||
|                 return false; |                 return false; | ||||||
|  |         check_eqc_bool_assignment(); | ||||||
|  |         check_missing_bool_enode_propagation(); | ||||||
|  |         m_egraph.invariant(); | ||||||
|         return true;  |         return true;  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -411,9 +497,9 @@ namespace euf { | ||||||
|     unsigned solver::max_var(unsigned w) const {  |     unsigned solver::max_var(unsigned w) const {  | ||||||
|         for (auto* e : m_solvers) |         for (auto* e : m_solvers) | ||||||
|             w = e->max_var(w); |             w = e->max_var(w); | ||||||
|         for (unsigned sz = m_var2node.size(); sz-- > 0; ) { |         for (unsigned sz = m_var2expr.size(); sz-- > 0; ) { | ||||||
|             euf::enode* n = m_var2node[sz]; |             expr* n = m_var2expr[sz]; | ||||||
|             if (n && m.is_bool(n->get_owner())) { |             if (n && m.is_bool(n)) { | ||||||
|                 w = std::max(w, sz); |                 w = std::max(w, sz); | ||||||
|                 break; |                 break; | ||||||
|             }            |             }            | ||||||
|  | @ -422,21 +508,15 @@ namespace euf { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     double solver::get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const { |     double solver::get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const { | ||||||
|         double r = 0; |         auto* ext = sat::constraint_base::to_extension(idx); | ||||||
|         for (auto* e : m_solvers) { |         SASSERT(ext);         | ||||||
|             r = e->get_reward(l, idx, occs); |         return (ext == this) ? 0 : ext->get_reward(l, idx, occs);         | ||||||
|             if (r != 0) |  | ||||||
|                 return r; |  | ||||||
|         } |  | ||||||
|         return r; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool solver::is_extended_binary(ext_justification_idx idx, literal_vector& r) { |     bool solver::is_extended_binary(ext_justification_idx idx, literal_vector& r) { | ||||||
|         for (auto* e : m_solvers) { |         auto* ext = sat::constraint_base::to_extension(idx); | ||||||
|             if (e->is_extended_binary(idx, r)) |         SASSERT(ext);         | ||||||
|                 return true; |         return (ext != this) && ext->is_extended_binary(idx, r); | ||||||
|         } |  | ||||||
|         return false; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void solver::init_ackerman() { |     void solver::init_ackerman() { | ||||||
|  |  | ||||||
|  | @ -48,41 +48,52 @@ namespace euf { | ||||||
|         size_t to_index() const { return sat::constraint_base::mem2base(this); } |         size_t to_index() const { return sat::constraint_base::mem2base(this); } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     class solver : public sat::extension, public th_internalizer, public th_decompile { |     class solver : public sat::extension, public th_internalizer, public th_decompile { | ||||||
|         typedef top_sort<euf::enode> deps_t; |         typedef top_sort<euf::enode> deps_t; | ||||||
|         friend class ackerman; |         friend class ackerman; | ||||||
|         // friend class sat::ba_solver;
 |         // friend class sat::ba_solver;
 | ||||||
|         struct stats { |         struct stats { | ||||||
|             unsigned m_num_dynack; |             unsigned m_ackerman; | ||||||
|             stats() { reset(); } |             stats() { reset(); } | ||||||
|             void reset() { memset(this, 0, sizeof(*this)); } |             void reset() { memset(this, 0, sizeof(*this)); } | ||||||
|         }; |         }; | ||||||
|         struct scope { |         struct scope { | ||||||
|             unsigned m_var_lim; |             unsigned m_var_lim; | ||||||
|             unsigned m_trail_lim; |  | ||||||
|         }; |         }; | ||||||
|         typedef ptr_vector<trail<solver> > trail_stack; |         typedef trail_stack<solver> euf_trail_stack; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast<size_t*>((size_t)(l.index() << 4)), 1); } | ||||||
|  |         size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); } | ||||||
|  |         bool is_literal(size_t* p) const { return GET_TAG(p) == 1; } | ||||||
|  |         bool is_justification(size_t* p) const { return GET_TAG(p) == 2; } | ||||||
|  |         sat::literal get_literal(size_t* p) { | ||||||
|  |             unsigned idx = static_cast<unsigned>(reinterpret_cast<size_t>(UNTAG(size_t*, p))); | ||||||
|  |             return sat::to_literal(idx >> 4); | ||||||
|  |         } | ||||||
|  |         size_t get_justification(size_t* p) const { | ||||||
|  |             return reinterpret_cast<size_t>(UNTAG(size_t*, p)); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         ast_manager&          m; |         ast_manager&          m; | ||||||
|         atom2bool_var&        m_expr2var; |  | ||||||
|         sat::sat_internalizer& si; |         sat::sat_internalizer& si; | ||||||
|         smt_params            m_config; |         smt_params            m_config; | ||||||
|         bool                  m_drat { false }; |  | ||||||
|         euf::egraph           m_egraph; |         euf::egraph           m_egraph; | ||||||
|  |         euf_trail_stack       m_trail; | ||||||
|         stats                 m_stats; |         stats                 m_stats; | ||||||
|         region                m_region; |  | ||||||
|         func_decl_ref_vector  m_unhandled_functions; |         func_decl_ref_vector  m_unhandled_functions; | ||||||
|         trail_stack           m_trail; |          | ||||||
| 
 | 
 | ||||||
|         sat::solver*           m_solver { nullptr }; |         sat::solver*           m_solver { nullptr }; | ||||||
|         sat::lookahead*        m_lookahead { nullptr }; |         sat::lookahead*        m_lookahead { nullptr }; | ||||||
|         ast_manager*           m_to_m; |         ast_manager*           m_to_m; | ||||||
|         atom2bool_var*         m_to_expr2var; |  | ||||||
|         sat::sat_internalizer* m_to_si; |         sat::sat_internalizer* m_to_si; | ||||||
|         scoped_ptr<euf::ackerman>   m_ackerman; |         scoped_ptr<euf::ackerman>   m_ackerman; | ||||||
| 
 | 
 | ||||||
|         ptr_vector<euf::enode>                          m_var2node; |         ptr_vector<expr>                                m_var2expr; | ||||||
|         ptr_vector<unsigned>                            m_explain; |         ptr_vector<size_t>                              m_explain; | ||||||
|         unsigned                                        m_num_scopes { 0 }; |         unsigned                                        m_num_scopes { 0 }; | ||||||
|         unsigned_vector                                 m_var_trail; |         unsigned_vector                                 m_var_trail; | ||||||
|         svector<scope>                                  m_scopes; |         svector<scope>                                  m_scopes; | ||||||
|  | @ -93,26 +104,29 @@ namespace euf { | ||||||
|         constraint* m_eq       { nullptr }; |         constraint* m_eq       { nullptr }; | ||||||
|         constraint* m_lit      { nullptr }; |         constraint* m_lit      { nullptr }; | ||||||
| 
 | 
 | ||||||
|         sat::solver& s() { return *m_solver; } |  | ||||||
|         unsigned * base_ptr() { return reinterpret_cast<unsigned*>(this); } |  | ||||||
| 
 |  | ||||||
|         // internalization
 |         // internalization
 | ||||||
| 
 |  | ||||||
|         bool visit(expr* e) override; |         bool visit(expr* e) override; | ||||||
|         bool visited(expr* e) override; |         bool visited(expr* e) override; | ||||||
|         bool post_visit(expr* e, bool sign, bool root) override;         |         bool post_visit(expr* e, bool sign, bool root) override;         | ||||||
|         void attach_node(euf::enode* n); |         sat::literal attach_lit(sat::literal lit, expr* e); | ||||||
|         void attach_lit(sat::literal lit, euf::enode* n); |  | ||||||
|         void add_distinct_axiom(app* e, euf::enode* const* args); |         void add_distinct_axiom(app* e, euf::enode* const* args); | ||||||
|         void add_not_distinct_axiom(app* e, euf::enode* const* args); |         void add_not_distinct_axiom(app* e, euf::enode* const* args); | ||||||
|         void axiomatize_basic(enode* n); |         void axiomatize_basic(enode* n); | ||||||
|         bool internalize_root(app* e, bool sign); |         bool internalize_root(app* e, bool sign, ptr_vector<enode> const& args); | ||||||
|         euf::enode* mk_true(); |         euf::enode* mk_true(); | ||||||
|         euf::enode* mk_false(); |         euf::enode* mk_false(); | ||||||
| 
 | 
 | ||||||
|  |         // replay
 | ||||||
|  |         expr_ref_vector      m_reinit_exprs; | ||||||
|  |         sat::bool_var_vector m_reinit_vars; | ||||||
|  |          | ||||||
|  |         void start_reinit(unsigned num_scopes); | ||||||
|  |         void finish_reinit(); | ||||||
| 
 | 
 | ||||||
|         // extensions
 |         // extensions
 | ||||||
|         th_solver* get_solver(func_decl* f); |         th_solver* get_solver(family_id fid, func_decl* f); | ||||||
|  |         th_solver* get_solver(sort* s) { return get_solver(s->get_family_id(), nullptr); } | ||||||
|  |         th_solver* get_solver(func_decl* f) { return get_solver(f->get_family_id(), f); } | ||||||
|         th_solver* get_solver(expr* e); |         th_solver* get_solver(expr* e); | ||||||
|         th_solver* get_solver(sat::bool_var v); |         th_solver* get_solver(sat::bool_var v); | ||||||
|         void add_solver(family_id fid, th_solver* th); |         void add_solver(family_id fid, th_solver* th); | ||||||
|  | @ -127,15 +141,20 @@ namespace euf { | ||||||
|         void values2model(deps_t const& deps, expr_ref_vector const& values, model_ref& mdl); |         void values2model(deps_t const& deps, expr_ref_vector const& values, model_ref& mdl); | ||||||
| 
 | 
 | ||||||
|         // solving
 |         // solving
 | ||||||
|         void propagate(); |  | ||||||
|         void propagate_literals(); |         void propagate_literals(); | ||||||
|         void propagate_th_eqs(); |         void propagate_th_eqs(); | ||||||
|         void get_antecedents(literal l, constraint& j, literal_vector& r); |         void get_antecedents(literal l, constraint& j, literal_vector& r, bool probing); | ||||||
|         void force_push(); | 
 | ||||||
|  |         // proofs
 | ||||||
|         void log_antecedents(std::ostream& out, literal l, literal_vector const& r); |         void log_antecedents(std::ostream& out, literal l, literal_vector const& r); | ||||||
|         void log_antecedents(literal l, literal_vector const& r); |         void log_antecedents(literal l, literal_vector const& r); | ||||||
|         void log_node(enode* n); |         void log_node(expr* n); | ||||||
|         void log_bool_var(sat::bool_var v, enode* n); |         bool m_drat_initialized{ false }; | ||||||
|  |         void init_drat(); | ||||||
|  | 
 | ||||||
|  |         // invariant
 | ||||||
|  |         void check_eqc_bool_assignment() const; | ||||||
|  |         void check_missing_bool_enode_propagation() const;         | ||||||
| 
 | 
 | ||||||
|         constraint& mk_constraint(constraint*& c, constraint::kind_t k); |         constraint& mk_constraint(constraint*& c, constraint::kind_t k); | ||||||
|         constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } |         constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } | ||||||
|  | @ -143,17 +162,17 @@ namespace euf { | ||||||
|         constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); } |         constraint& lit_constraint() { return mk_constraint(m_lit, constraint::kind_t::lit); } | ||||||
| 
 | 
 | ||||||
|     public: |     public: | ||||||
|        solver(ast_manager& m, atom2bool_var& expr2var, sat::sat_internalizer& si, params_ref const& p = params_ref()): |        solver(ast_manager& m, sat::sat_internalizer& si, params_ref const& p = params_ref()): | ||||||
|             m(m), |             m(m), | ||||||
|             m_expr2var(expr2var), |  | ||||||
|             si(si), |             si(si), | ||||||
|             m_egraph(m), |             m_egraph(m), | ||||||
|  |             m_trail(*this), | ||||||
|             m_unhandled_functions(m), |             m_unhandled_functions(m), | ||||||
|             m_solver(nullptr), |             m_solver(nullptr), | ||||||
|             m_lookahead(nullptr), |             m_lookahead(nullptr), | ||||||
|             m_to_m(&m), |             m_to_m(&m), | ||||||
|             m_to_expr2var(&expr2var), |             m_to_si(&si), | ||||||
|             m_to_si(&si) |             m_reinit_exprs(m) | ||||||
|         { |         { | ||||||
|             updt_params(p); |             updt_params(p); | ||||||
|         } |         } | ||||||
|  | @ -166,37 +185,45 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|        struct scoped_set_translate { |        struct scoped_set_translate { | ||||||
|            solver& s; |            solver& s; | ||||||
|            scoped_set_translate(solver& s, ast_manager& m, atom2bool_var& a2b, sat::sat_internalizer& si) : |            scoped_set_translate(solver& s, ast_manager& m, sat::sat_internalizer& si) : | ||||||
|                s(s) { |                s(s) { | ||||||
|                s.m_to_m = &m; |                s.m_to_m = &m; | ||||||
|                s.m_to_expr2var = &a2b; |  | ||||||
|                s.m_to_si = &si; |                s.m_to_si = &si; | ||||||
|            } |            } | ||||||
|            ~scoped_set_translate() { |            ~scoped_set_translate() { | ||||||
|                s.m_to_m = &s.m; |                s.m_to_m = &s.m; | ||||||
|                s.m_to_expr2var = &s.m_expr2var; |  | ||||||
|                s.m_to_si = &s.si; |                s.m_to_si = &s.si; | ||||||
|            } |            } | ||||||
|        }; |        }; | ||||||
| 
 | 
 | ||||||
|  |         sat::solver& s() { return *m_solver; } | ||||||
|  |         sat::solver const& s() const { return *m_solver; } | ||||||
|         sat::sat_internalizer& get_si() { return si; } |         sat::sat_internalizer& get_si() { return si; } | ||||||
|         ast_manager& get_manager() { return m; } |         ast_manager& get_manager() { return m; } | ||||||
|         enode* get_enode(expr* e) { return m_egraph.find(e); } |         enode* get_enode(expr* e) { return m_egraph.find(e); } | ||||||
|         sat::literal get_literal(expr* e) { return literal(m_expr2var.to_bool_var(e), false); } |         sat::literal get_literal(expr* e) const { return literal(si.to_bool_var(e), false); } | ||||||
|  |         sat::literal get_literal(enode* e) const { return get_literal(e->get_owner()); } | ||||||
|         smt_params const& get_config() { return m_config; } |         smt_params const& get_config() { return m_config; } | ||||||
|         region& get_region() { return m_region; } |         region& get_region() { return m_trail.get_region(); } | ||||||
|         template <typename C> |         template <typename C> | ||||||
|         void push(C const& c) { m_trail.push_back(new (m_region) C(c));  } |         void push(C const& c) { m_trail.push(c);  } | ||||||
|  |         euf_trail_stack& get_trail_stack() { return m_trail; } | ||||||
| 
 | 
 | ||||||
|         void updt_params(params_ref const& p); |         void updt_params(params_ref const& p); | ||||||
|         void set_solver(sat::solver* s) override { m_solver = s; m_drat = s->get_config().m_drat; } |         void set_solver(sat::solver* s) override { m_solver = s; } | ||||||
|         void set_lookahead(sat::lookahead* s) override { m_lookahead = s; } |         void set_lookahead(sat::lookahead* s) override { m_lookahead = s; } | ||||||
|         void init_search() override; |         void init_search() override; | ||||||
|         double get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const override; |         double get_reward(literal l, ext_constraint_idx idx, sat::literal_occs_fun& occs) const override; | ||||||
|         bool is_extended_binary(ext_justification_idx idx, literal_vector& r) override; |         bool is_extended_binary(ext_justification_idx idx, literal_vector& r) override; | ||||||
|         bool is_external(bool_var v) override; |         bool is_external(bool_var v) override; | ||||||
|         bool propagate(literal l, ext_constraint_idx idx) override; |         bool propagate(literal l, ext_constraint_idx idx) override; | ||||||
|         void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override; |         bool unit_propagate() override; | ||||||
|  |         void propagate(enode* a, enode* b, ext_justification_idx); | ||||||
|  |         bool set_root(literal l, literal r) override; | ||||||
|  |         void flush_roots() override; | ||||||
|  | 
 | ||||||
|  |         void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r, bool probing) override; | ||||||
|  |         void add_antecedent(enode* a, enode* b); | ||||||
|         void asserted(literal l) override; |         void asserted(literal l) override; | ||||||
|         sat::check_result check() override; |         sat::check_result check() override; | ||||||
|         void push() override; |         void push() override; | ||||||
|  | @ -220,6 +247,9 @@ namespace euf { | ||||||
|         bool check_model(sat::model const& m) const override; |         bool check_model(sat::model const& m) const override; | ||||||
|         unsigned max_var(unsigned w) const override; |         unsigned max_var(unsigned w) const override; | ||||||
| 
 | 
 | ||||||
|  |         bool use_drat() { return s().get_config().m_drat && (init_drat(), true);  } | ||||||
|  |         sat::drat& get_drat() { return s().get_drat(); } | ||||||
|  | 
 | ||||||
|         // decompile
 |         // decompile
 | ||||||
|         bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card, |         bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card, | ||||||
|                         std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override; |                         std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& pb) override; | ||||||
|  | @ -228,11 +258,18 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|         // internalize
 |         // internalize
 | ||||||
|         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; |         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; | ||||||
|  |         void internalize(expr* e, bool learned) override; | ||||||
|         void attach_th_var(enode* n, th_solver* th, theory_var v) { m_egraph.add_th_var(n, v, th->get_id()); } |         void attach_th_var(enode* n, th_solver* th, theory_var v) { m_egraph.add_th_var(n, v, th->get_id()); } | ||||||
|  |         void attach_node(euf::enode* n); | ||||||
|         euf::enode* mk_enode(expr* e, unsigned n, enode* const* args) { return m_egraph.mk(e, n, args); } |         euf::enode* mk_enode(expr* e, unsigned n, enode* const* args) { return m_egraph.mk(e, n, args); } | ||||||
|  |         expr* bool_var2expr(sat::bool_var v) { return m_var2expr.get(v, nullptr); } | ||||||
| 
 | 
 | ||||||
|         void update_model(model_ref& mdl); |         void update_model(model_ref& mdl); | ||||||
| 
 | 
 | ||||||
|         func_decl_ref_vector const& unhandled_functions() { return m_unhandled_functions; }        |         func_decl_ref_vector const& unhandled_functions() { return m_unhandled_functions; }        | ||||||
|     };     |     };     | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) { | ||||||
|  |     return s.display(out); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -39,8 +39,11 @@ namespace sat { | ||||||
|         virtual ~sat_internalizer() {} |         virtual ~sat_internalizer() {} | ||||||
|         virtual bool is_bool_op(expr* e) const = 0; |         virtual bool is_bool_op(expr* e) const = 0; | ||||||
|         virtual literal internalize(expr* e, bool learned) = 0; |         virtual literal internalize(expr* e, bool learned) = 0; | ||||||
|  |         virtual bool_var to_bool_var(expr* e) = 0; | ||||||
|         virtual bool_var add_bool_var(expr* e)  = 0; |         virtual bool_var add_bool_var(expr* e)  = 0; | ||||||
|         virtual void cache(app* t, literal l) = 0; |         virtual void cache(app* t, literal l) = 0; | ||||||
|  |         virtual void push() = 0; | ||||||
|  |         virtual void pop(unsigned n) = 0; | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     class constraint_base { |     class constraint_base { | ||||||
|  |  | ||||||
|  | @ -65,15 +65,24 @@ namespace euf { | ||||||
|         return ctx.get_region(); |         return ctx.get_region(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     enode* th_euf_solver::get_enode(expr* e) const {  |     trail_stack<euf::solver>& th_euf_solver::get_trail_stack() { | ||||||
|  |         return ctx.get_trail_stack(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     enode* th_euf_solver::expr2enode(expr* e) const {  | ||||||
|         return ctx.get_enode(e);  |         return ctx.get_enode(e);  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     sat::literal th_euf_solver::get_literal(expr* e) const { |     sat::literal th_euf_solver::expr2literal(expr* e) const { | ||||||
|         return ctx.get_literal(e); |         return ctx.get_literal(e); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     expr* th_euf_solver::bool_var2expr(sat::bool_var v) const { | ||||||
|  |         return ctx.bool_var2expr(v); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     theory_var th_euf_solver::mk_var(enode * n) { |     theory_var th_euf_solver::mk_var(enode * n) { | ||||||
|  |         force_push(); | ||||||
|         SASSERT(!is_attached_to_var(n)); |         SASSERT(!is_attached_to_var(n)); | ||||||
|         euf::theory_var v = m_var2enode.size(); |         euf::theory_var v = m_var2enode.size(); | ||||||
|         m_var2enode.push_back(n); |         m_var2enode.push_back(n); | ||||||
|  | @ -82,7 +91,7 @@ namespace euf { | ||||||
| 
 | 
 | ||||||
|     bool th_euf_solver::is_attached_to_var(enode* n) const { |     bool th_euf_solver::is_attached_to_var(enode* n) const { | ||||||
|         theory_var v = n->get_th_var(get_id()); |         theory_var v = n->get_th_var(get_id()); | ||||||
|         return v != null_theory_var && get_enode(v) == n; |         return v != null_theory_var && var2enode(v) == n; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     theory_var th_euf_solver::get_th_var(expr* e) const { |     theory_var th_euf_solver::get_th_var(expr* e) const { | ||||||
|  | @ -99,4 +108,35 @@ namespace euf { | ||||||
|         m_var2enode_lim.shrink(new_lvl); |         m_var2enode_lim.shrink(new_lvl); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     unsigned th_euf_solver::lazy_pop(unsigned n) { | ||||||
|  |         if (n <= m_num_scopes) { | ||||||
|  |             m_num_scopes -= n; | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             n -= m_num_scopes; | ||||||
|  |             pop(n); | ||||||
|  |             return n; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void th_euf_solver::add_unit(sat::literal lit) { | ||||||
|  |         ctx.s().add_clause(1, &lit, sat::status::th(m_is_redundant, get_id()));  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void th_euf_solver::add_clause(sat::literal a, sat::literal b) { | ||||||
|  |         sat::literal lits[2] = { a, b }; | ||||||
|  |         ctx.s().add_clause(2, lits, sat::status::th(m_is_redundant, get_id())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) { | ||||||
|  |         sat::literal lits[3] = { a, b, c }; | ||||||
|  |         ctx.s().add_clause(3, lits, sat::status::th(m_is_redundant, get_id())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) { | ||||||
|  |         sat::literal lits[4] = { a, b, c, d }; | ||||||
|  |         ctx.s().add_clause(4, lits, sat::status::th(m_is_redundant, get_id())); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,11 +36,14 @@ namespace euf { | ||||||
|         virtual bool visit(expr* e) { return false; } |         virtual bool visit(expr* e) { return false; } | ||||||
|         virtual bool visited(expr* e) { return false; } |         virtual bool visited(expr* e) { return false; } | ||||||
|         virtual bool post_visit(expr* e, bool sign, bool root) { return false; } |         virtual bool post_visit(expr* e, bool sign, bool root) { return false; } | ||||||
|  | 
 | ||||||
|     public: |     public: | ||||||
|         virtual ~th_internalizer() {} |         virtual ~th_internalizer() {} | ||||||
| 
 | 
 | ||||||
|         virtual sat::literal internalize(expr* e, bool sign, bool root, bool redundant) = 0; |         virtual sat::literal internalize(expr* e, bool sign, bool root, bool redundant) = 0; | ||||||
| 
 | 
 | ||||||
|  |         virtual void internalize(expr* e, bool redundant) = 0; | ||||||
|  | 
 | ||||||
|         /**
 |         /**
 | ||||||
|            \brief Apply (interpreted) sort constraints on the given enode. |            \brief Apply (interpreted) sort constraints on the given enode. | ||||||
|         */ |         */ | ||||||
|  | @ -69,7 +72,7 @@ namespace euf { | ||||||
|         /**
 |         /**
 | ||||||
|            \brief compute dependencies for node n |            \brief compute dependencies for node n | ||||||
|          */ |          */ | ||||||
|         virtual void add_dep(euf::enode* n, top_sort<euf::enode>& dep) {} |         virtual void add_dep(euf::enode* n, top_sort<euf::enode>& dep) { dep.insert(n, nullptr);  } | ||||||
| 
 | 
 | ||||||
|         /**
 |         /**
 | ||||||
|            \brief should function be included in model. |            \brief should function be included in model. | ||||||
|  | @ -102,24 +105,39 @@ namespace euf { | ||||||
|         solver &            ctx; |         solver &            ctx; | ||||||
|         euf::enode_vector   m_var2enode; |         euf::enode_vector   m_var2enode; | ||||||
|         unsigned_vector     m_var2enode_lim; |         unsigned_vector     m_var2enode_lim; | ||||||
|  |         unsigned            m_num_scopes{ 0 }; | ||||||
| 
 | 
 | ||||||
|         smt_params const& get_config() const; |         smt_params const& get_config() const; | ||||||
|         sat::literal get_literal(expr* e) const; |         sat::literal expr2literal(expr* e) const; | ||||||
|         region& get_region(); |         region& get_region(); | ||||||
|  |          | ||||||
|  | 
 | ||||||
|  |         void add_unit(sat::literal lit); | ||||||
|  |         void add_clause(sat::literal a, sat::literal b); | ||||||
|  |         void add_clause(sat::literal a, sat::literal b, sat::literal c); | ||||||
|  |         void add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d); | ||||||
|  | 
 | ||||||
|     public: |     public: | ||||||
|         th_euf_solver(euf::solver& ctx, euf::theory_id id); |         th_euf_solver(euf::solver& ctx, euf::theory_id id); | ||||||
|         virtual ~th_euf_solver() {} |         virtual ~th_euf_solver() {} | ||||||
|         virtual theory_var mk_var(enode * n); |         virtual theory_var mk_var(enode * n); | ||||||
|         unsigned get_num_vars() const { return m_var2enode.size();} |         unsigned get_num_vars() const { return m_var2enode.size();} | ||||||
|         enode* get_enode(expr* e) const;  |         enode* expr2enode(expr* e) const;  | ||||||
|         enode* get_enode(theory_var v) const { return m_var2enode[v]; } |         enode* var2enode(theory_var v) const { return m_var2enode[v]; } | ||||||
|         expr* get_expr(theory_var v) const { return get_enode(v)->get_owner(); } |         expr* var2expr(theory_var v) const { return var2enode(v)->get_owner(); } | ||||||
|         expr_ref get_expr(sat::literal lit) const { expr* e = get_expr(lit.var()); return lit.sign() ? expr_ref(m.mk_not(e), m) : expr_ref(e, m); } |         expr* bool_var2expr(sat::bool_var v) const; | ||||||
|  |         expr_ref literal2expr(sat::literal lit) const { expr* e = bool_var2expr(lit.var()); return lit.sign() ? expr_ref(m.mk_not(e), m) : expr_ref(e, m); } | ||||||
|         theory_var get_th_var(enode* n) const { return n->get_th_var(get_id()); } |         theory_var get_th_var(enode* n) const { return n->get_th_var(get_id()); } | ||||||
|         theory_var get_th_var(expr* e) const; |         theory_var get_th_var(expr* e) const; | ||||||
|  |         trail_stack<euf::solver> & get_trail_stack(); | ||||||
|         bool is_attached_to_var(enode* n) const; |         bool is_attached_to_var(enode* n) const; | ||||||
|  |         bool is_root(theory_var v) const { return var2enode(v)->is_root(); } | ||||||
|         void push() override; |         void push() override; | ||||||
|         void pop(unsigned n) override; |         void pop(unsigned n) override; | ||||||
|  | 
 | ||||||
|  |         void lazy_push() { ++m_num_scopes; } | ||||||
|  |         void force_push() { for (; m_num_scopes > 0; --m_num_scopes) push(); } | ||||||
|  |         unsigned lazy_pop(unsigned n); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -29,6 +29,7 @@ Notes: | ||||||
| #include "util/ref_util.h" | #include "util/ref_util.h" | ||||||
| #include "ast/ast_smt2_pp.h" | #include "ast/ast_smt2_pp.h" | ||||||
| #include "ast/ast_pp.h" | #include "ast/ast_pp.h" | ||||||
|  | #include "ast/ast_smt2_pp.h" | ||||||
| #include "ast/ast_ll_pp.h" | #include "ast/ast_ll_pp.h" | ||||||
| #include "ast/pb_decl_plugin.h" | #include "ast/pb_decl_plugin.h" | ||||||
| #include "ast/ast_util.h" | #include "ast/ast_util.h" | ||||||
|  | @ -38,6 +39,7 @@ Notes: | ||||||
| #include "tactic/tactic.h" | #include "tactic/tactic.h" | ||||||
| #include "tactic/generic_model_converter.h" | #include "tactic/generic_model_converter.h" | ||||||
| #include "sat/sat_cut_simplifier.h" | #include "sat/sat_cut_simplifier.h" | ||||||
|  | #include "sat/sat_drat.h" | ||||||
| #include "sat/tactic/goal2sat.h" | #include "sat/tactic/goal2sat.h" | ||||||
| #include "sat/smt/ba_solver.h" | #include "sat/smt/ba_solver.h" | ||||||
| #include "sat/smt/euf_solver.h" | #include "sat/smt/euf_solver.h" | ||||||
|  | @ -72,6 +74,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|     bool                        m_default_external; |     bool                        m_default_external; | ||||||
|     bool                        m_xor_solver; |     bool                        m_xor_solver; | ||||||
|     bool                        m_euf; |     bool                        m_euf; | ||||||
|  |     bool                        m_drat; | ||||||
|     bool                        m_is_redundant { false }; |     bool                        m_is_redundant { false }; | ||||||
|     sat::literal_vector         aig_lits; |     sat::literal_vector         aig_lits; | ||||||
|      |      | ||||||
|  | @ -98,6 +101,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); |         m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); | ||||||
|         m_xor_solver = p.get_bool("xor_solver", false); |         m_xor_solver = p.get_bool("xor_solver", false); | ||||||
|         m_euf = sp.euf(); |         m_euf = sp.euf(); | ||||||
|  |         m_drat = sp.drat_file() != symbol(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void throw_op_not_handled(std::string const& s) { |     void throw_op_not_handled(std::string const& s) { | ||||||
|  | @ -125,19 +129,71 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         m_solver.add_clause(num, lits, m_is_redundant ? sat::status::redundant() : sat::status::asserted()); |         m_solver.add_clause(num, lits, m_is_redundant ? sat::status::redundant() : sat::status::asserted()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void mk_root_clause(sat::literal l) { | ||||||
|  |         TRACE("goal2sat", tout << "mk_clause: " << l << "\n";); | ||||||
|  |         m_solver.add_clause(1, &l, m_is_redundant ? sat::status::redundant() : sat::status::input()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void mk_root_clause(sat::literal l1, sat::literal l2) { | ||||||
|  |         TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";); | ||||||
|  |         m_solver.add_clause(l1, l2, m_is_redundant ? sat::status::redundant() : sat::status::input()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void mk_root_clause(sat::literal l1, sat::literal l2, sat::literal l3) { | ||||||
|  |         TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";); | ||||||
|  |         m_solver.add_clause(l1, l2, l3, m_is_redundant ? sat::status::redundant() : sat::status::input()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void mk_root_clause(unsigned num, sat::literal * lits) { | ||||||
|  |         TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";); | ||||||
|  |         m_solver.add_clause(num, lits, m_is_redundant ? sat::status::redundant() : sat::status::input()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     sat::bool_var add_var(bool is_ext, expr* n) { | ||||||
|  |         auto v = m_solver.add_var(is_ext); | ||||||
|  |         log_node(v, n); | ||||||
|  |         return v; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void log_node(sat::bool_var v, expr* n) { | ||||||
|  |         if (m_drat && m_solver.get_drat_ptr()) { | ||||||
|  |             sat::drat* drat = m_solver.get_drat_ptr(); | ||||||
|  |             if (is_app(n)) { | ||||||
|  |                 app* a = to_app(n); | ||||||
|  |                 std::stringstream strm; | ||||||
|  |                 strm << mk_ismt2_func(a->get_decl(), m); | ||||||
|  |                 drat->def_begin(n->get_id(), strm.str()); | ||||||
|  |                 for (expr* arg : *a) | ||||||
|  |                     drat->def_add_arg(arg->get_id()); | ||||||
|  |                 drat->def_end(); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 IF_VERBOSE(0, verbose_stream() << "skipping DRAT of non-app\n"); | ||||||
|  |             } | ||||||
|  |             drat->bool_def(v, n->get_id()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     sat::literal mk_true() { |     sat::literal mk_true() { | ||||||
|         if (m_true == sat::null_literal) { |         if (m_true == sat::null_literal) { | ||||||
|             // create fake variable to represent true;
 |             // create fake variable to represent true;
 | ||||||
|             m_true = sat::literal(m_solver.add_var(false), false); |             m_true = sat::literal(add_var(false, m.mk_true()), false); | ||||||
|             mk_clause(m_true); // v is true
 |             mk_clause(m_true); // v is true
 | ||||||
|         } |         } | ||||||
|         return m_true; |         return m_true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     sat::bool_var to_bool_var(expr* e) override { | ||||||
|  |         return m_map.to_bool_var(e); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     sat::bool_var add_bool_var(expr* t) override { |     sat::bool_var add_bool_var(expr* t) override { | ||||||
|         sat::bool_var v = m_map.to_bool_var(t); |         sat::bool_var v = m_map.to_bool_var(t); | ||||||
|         if (v == sat::null_bool_var) { |         if (v == sat::null_bool_var) { | ||||||
|             v = m_solver.add_var(true); |             v = add_var(true, t); | ||||||
|  |             force_push(); | ||||||
|             m_map.insert(t, v); |             m_map.insert(t, v); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|  | @ -146,6 +202,28 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         return v; |         return v; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     unsigned m_num_scopes{ 0 }; | ||||||
|  | 
 | ||||||
|  |     void force_push() { | ||||||
|  |         for (; m_num_scopes > 0; --m_num_scopes) | ||||||
|  |             m_map.push(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void push() override { | ||||||
|  |         ++m_num_scopes; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void pop(unsigned n) override { | ||||||
|  |         if (n <= m_num_scopes) { | ||||||
|  |             m_num_scopes -= n; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         n -= m_num_scopes; | ||||||
|  |         m_num_scopes = 0; | ||||||
|  |         m_cache.reset(); | ||||||
|  |         m_map.pop(n); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void cache(app* t, sat::literal l) override { |     void cache(app* t, sat::literal l) override { | ||||||
|         m_cache.insert(t, l); |         m_cache.insert(t, l); | ||||||
|     } |     } | ||||||
|  | @ -167,11 +245,6 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|                 throw_op_not_handled(strm.str()); |                 throw_op_not_handled(strm.str()); | ||||||
|             } |             } | ||||||
|             else {                 |             else {                 | ||||||
|                 bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t); |  | ||||||
|                 v = m_solver.add_var(ext); |  | ||||||
|                 m_map.insert(t, v); |  | ||||||
|                 l = sat::literal(v, sign); |  | ||||||
|                 TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << " " << is_uninterp_const(t) << "\n";); |  | ||||||
|                 if (!is_uninterp_const(t)) { |                 if (!is_uninterp_const(t)) { | ||||||
|                     if (m_euf) { |                     if (m_euf) { | ||||||
|                         convert_euf(t, root, sign);                         |                         convert_euf(t, root, sign);                         | ||||||
|  | @ -180,6 +253,12 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|                     else |                     else | ||||||
|                         m_unhandled_funs.push_back(to_app(t)->get_decl()); |                         m_unhandled_funs.push_back(to_app(t)->get_decl()); | ||||||
|                 } |                 } | ||||||
|  |                 bool ext = m_default_external || !is_uninterp_const(t) || m_interface_vars.contains(t); | ||||||
|  |                 v = add_var(ext, t); | ||||||
|  |                 force_push(); | ||||||
|  |                 m_map.insert(t, v); | ||||||
|  |                 l = sat::literal(v, sign); | ||||||
|  |                 TRACE("sat", tout << "new_var: " << v << ": " << mk_bounded_pp(t, m, 2) << " " << is_uninterp_const(t) << "\n";); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|  | @ -191,7 +270,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|             m_result_stack.reset(); |             m_result_stack.reset(); | ||||||
|         SASSERT(l != sat::null_literal); |         SASSERT(l != sat::null_literal); | ||||||
|         if (root) |         if (root) | ||||||
|             mk_clause(l); |             mk_root_clause(l); | ||||||
|         else |         else | ||||||
|             m_result_stack.push_back(l); |             m_result_stack.push_back(l); | ||||||
|     } |     } | ||||||
|  | @ -213,7 +292,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|             if (sign) |             if (sign) | ||||||
|                 l.neg(); |                 l.neg(); | ||||||
|             if (root) |             if (root) | ||||||
|                 mk_clause(l); |                 mk_root_clause(l); | ||||||
|             else |             else | ||||||
|                 m_result_stack.push_back(l); |                 m_result_stack.push_back(l); | ||||||
|             return true; |             return true; | ||||||
|  | @ -275,17 +354,17 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|                 for (unsigned i = 0; i < num; i++) { |                 for (unsigned i = 0; i < num; i++) { | ||||||
|                     sat::literal l = m_result_stack[i]; |                     sat::literal l = m_result_stack[i]; | ||||||
|                     l.neg(); |                     l.neg(); | ||||||
|                     mk_clause(l); |                     mk_root_clause(l); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); |                 mk_root_clause(m_result_stack.size(), m_result_stack.c_ptr()); | ||||||
|             } |             } | ||||||
|             m_result_stack.reset(); |             m_result_stack.reset(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             SASSERT(num <= m_result_stack.size()); |             SASSERT(num <= m_result_stack.size()); | ||||||
|             sat::bool_var k = m_solver.add_var(false); |             sat::bool_var k = add_var(false, t); | ||||||
|             sat::literal  l(k, false); |             sat::literal  l(k, false); | ||||||
|             m_cache.insert(t, l); |             m_cache.insert(t, l); | ||||||
|             sat::literal * lits = m_result_stack.end() - num;            |             sat::literal * lits = m_result_stack.end() - num;            | ||||||
|  | @ -321,18 +400,18 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|                 for (unsigned i = 0; i < num; ++i) { |                 for (unsigned i = 0; i < num; ++i) { | ||||||
|                     m_result_stack[i].neg(); |                     m_result_stack[i].neg(); | ||||||
|                 }                 |                 }                 | ||||||
|                 mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); |                 mk_root_clause(m_result_stack.size(), m_result_stack.c_ptr()); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 for (unsigned i = 0; i < num; ++i) { |                 for (unsigned i = 0; i < num; ++i) { | ||||||
|                     mk_clause(m_result_stack[i]); |                     mk_root_clause(m_result_stack[i]); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             m_result_stack.reset(); |             m_result_stack.reset(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             SASSERT(num <= m_result_stack.size()); |             SASSERT(num <= m_result_stack.size()); | ||||||
|             sat::bool_var k = m_solver.add_var(false); |             sat::bool_var k = add_var(false, t); | ||||||
|             sat::literal  l(k, false); |             sat::literal  l(k, false); | ||||||
|             m_cache.insert(t, l); |             m_cache.insert(t, l); | ||||||
|             sat::literal * lits = m_result_stack.end() - num; |             sat::literal * lits = m_result_stack.end() - num; | ||||||
|  | @ -373,17 +452,17 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         if (root) { |         if (root) { | ||||||
|             SASSERT(sz == 3); |             SASSERT(sz == 3); | ||||||
|             if (sign) { |             if (sign) { | ||||||
|                 mk_clause(~c, ~t); |                 mk_root_clause(~c, ~t); | ||||||
|                 mk_clause(c,  ~e); |                 mk_root_clause(c,  ~e); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 mk_clause(~c, t); |                 mk_root_clause(~c, t); | ||||||
|                 mk_clause(c, e); |                 mk_root_clause(c, e); | ||||||
|             } |             } | ||||||
|             m_result_stack.reset(); |             m_result_stack.reset(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             sat::bool_var k = m_solver.add_var(false); |             sat::bool_var k = add_var(false, n); | ||||||
|             sat::literal  l(k, false); |             sat::literal  l(k, false); | ||||||
|             m_cache.insert(n, l); |             m_cache.insert(n, l); | ||||||
|             mk_clause(~l, ~c, t); |             mk_clause(~l, ~c, t); | ||||||
|  | @ -411,16 +490,16 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         if (root) { |         if (root) { | ||||||
|             SASSERT(sz == 2); |             SASSERT(sz == 2); | ||||||
|             if (sign) { |             if (sign) { | ||||||
|                 mk_clause(l1); |                 mk_root_clause(l1); | ||||||
|                 mk_clause(~l2); |                 mk_root_clause(~l2); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 mk_clause(~l1, l2); |                 mk_root_clause(~l1, l2); | ||||||
|             } |             } | ||||||
|             m_result_stack.reset(); |             m_result_stack.reset(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             sat::bool_var k = m_solver.add_var(false); |             sat::bool_var k = add_var(false, t); | ||||||
|             sat::literal  l(k, false); |             sat::literal  l(k, false); | ||||||
|             m_cache.insert(t, l); |             m_cache.insert(t, l); | ||||||
|             // l <=> (l1 => l2)
 |             // l <=> (l1 => l2)
 | ||||||
|  | @ -443,17 +522,17 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         if (root) { |         if (root) { | ||||||
|             SASSERT(sz == 2); |             SASSERT(sz == 2); | ||||||
|             if (sign) { |             if (sign) { | ||||||
|                 mk_clause(l1, l2); |                 mk_root_clause(l1, l2); | ||||||
|                 mk_clause(~l1, ~l2); |                 mk_root_clause(~l1, ~l2); | ||||||
|             } |             } | ||||||
|             else { |             else { | ||||||
|                 mk_clause(l1, ~l2); |                 mk_root_clause(l1, ~l2); | ||||||
|                 mk_clause(~l1, l2); |                 mk_root_clause(~l1, l2); | ||||||
|             } |             } | ||||||
|             m_result_stack.reset(); |             m_result_stack.reset(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             sat::bool_var k = m_solver.add_var(false); |             sat::bool_var k = add_var(false, t); | ||||||
|             sat::literal  l(k, false); |             sat::literal  l(k, false); | ||||||
|             m_cache.insert(t, l); |             m_cache.insert(t, l); | ||||||
|             mk_clause(~l, l1, ~l2); |             mk_clause(~l, l1, ~l2); | ||||||
|  | @ -486,7 +565,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         sat::extension* ext = m_solver.get_extension(); |         sat::extension* ext = m_solver.get_extension(); | ||||||
|         euf::solver* euf = nullptr; |         euf::solver* euf = nullptr; | ||||||
|         if (!ext) { |         if (!ext) { | ||||||
|             euf = alloc(euf::solver, m, m_map, *this); |             euf = alloc(euf::solver, m, *this); | ||||||
|             m_solver.set_extension(euf); |             m_solver.set_extension(euf); | ||||||
|             for (unsigned i = m_solver.num_scopes(); i-- > 0; ) |             for (unsigned i = m_solver.num_scopes(); i-- > 0; ) | ||||||
|                 euf->push(); |                 euf->push(); | ||||||
|  | @ -502,7 +581,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         if (lit == sat::null_literal) |         if (lit == sat::null_literal) | ||||||
|             return; |             return; | ||||||
|         if (root) |         if (root) | ||||||
|             mk_clause(lit); |             mk_root_clause(lit); | ||||||
|         else |         else | ||||||
|             m_result_stack.push_back(lit); |             m_result_stack.push_back(lit); | ||||||
|     } |     } | ||||||
|  | @ -528,7 +607,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
|         if (lit == sat::null_literal) |         if (lit == sat::null_literal) | ||||||
|             return; |             return; | ||||||
|         if (root)  |         if (root)  | ||||||
|             mk_clause(lit);         |             mk_root_clause(lit);         | ||||||
|         else  |         else  | ||||||
|             m_result_stack.push_back(lit);       |             m_result_stack.push_back(lit);       | ||||||
|     } |     } | ||||||
|  | @ -643,6 +722,7 @@ struct goal2sat::imp : public sat::sat_internalizer { | ||||||
| 
 | 
 | ||||||
|     sat::literal internalize(expr* n, bool redundant) override { |     sat::literal internalize(expr* n, bool redundant) override { | ||||||
|         unsigned sz = m_result_stack.size(); |         unsigned sz = m_result_stack.size(); | ||||||
|  |         (void)sz; | ||||||
|         process(n, false, redundant); |         process(n, false, redundant); | ||||||
|         SASSERT(m_result_stack.size() == sz + 1); |         SASSERT(m_result_stack.size() == sz + 1); | ||||||
|         sat::literal result = m_result_stack.back(); |         sat::literal result = m_result_stack.back(); | ||||||
|  |  | ||||||
|  | @ -64,7 +64,8 @@ class sat_tactic : public tactic { | ||||||
|             TRACE("sat_dimacs", m_solver->display_dimacs(tout);); |             TRACE("sat_dimacs", m_solver->display_dimacs(tout);); | ||||||
|             dep2assumptions(dep2asm, assumptions); |             dep2assumptions(dep2asm, assumptions); | ||||||
|             lbool r = m_solver->check(assumptions.size(), assumptions.c_ptr()); |             lbool r = m_solver->check(assumptions.size(), assumptions.c_ptr()); | ||||||
|             TRACE("sat", tout << "result of checking: " << r << " " << m_solver->get_reason_unknown() << "\n";); |             TRACE("sat", tout << "result of checking: " << r << " ";  | ||||||
|  |                   if (r == l_undef) tout << m_solver->get_reason_unknown(); tout << "\n";); | ||||||
|             if (r == l_false) { |             if (r == l_false) { | ||||||
|                 expr_dependency * lcore = nullptr; |                 expr_dependency * lcore = nullptr; | ||||||
|                 if (produce_core) { |                 if (produce_core) { | ||||||
|  | @ -88,13 +89,18 @@ class sat_tactic : public tactic { | ||||||
|                     for (auto const& kv : map) { |                     for (auto const& kv : map) { | ||||||
|                         expr * n   = kv.m_key; |                         expr * n   = kv.m_key; | ||||||
|                         sat::bool_var v = kv.m_value; |                         sat::bool_var v = kv.m_value; | ||||||
|  |                         if (!is_app(n)) | ||||||
|  |                             continue; | ||||||
|  |                         app* a = to_app(n); | ||||||
|  |                         if (!is_uninterp_const(a)) | ||||||
|  |                             continue; | ||||||
|                         TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";); |                         TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";); | ||||||
|                         switch (sat::value_at(v, ll_m)) { |                         switch (sat::value_at(v, ll_m)) { | ||||||
|                         case l_true:  |                         case l_true:  | ||||||
|                             md->register_decl(to_app(n)->get_decl(), m.mk_true());  |                             md->register_decl(a->get_decl(), m.mk_true());  | ||||||
|                             break; |                             break; | ||||||
|                         case l_false: |                         case l_false: | ||||||
|                             md->register_decl(to_app(n)->get_decl(), m.mk_false()); |                             md->register_decl(a->get_decl(), m.mk_false()); | ||||||
|                             break; |                             break; | ||||||
|                         default: |                         default: | ||||||
|                             break; |                             break; | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ endforeach() | ||||||
| add_executable(shell | add_executable(shell | ||||||
|   datalog_frontend.cpp |   datalog_frontend.cpp | ||||||
|   dimacs_frontend.cpp |   dimacs_frontend.cpp | ||||||
|  |   drat_frontend.cpp | ||||||
|   "${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp" |   "${CMAKE_CURRENT_BINARY_DIR}/gparams_register_modules.cpp" | ||||||
|   "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp" |   "${CMAKE_CURRENT_BINARY_DIR}/install_tactic.cpp" | ||||||
|   main.cpp |   main.cpp | ||||||
|  |  | ||||||
							
								
								
									
										197
									
								
								src/shell/drat_frontend.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								src/shell/drat_frontend.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,197 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2020 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include<iostream> | ||||||
|  | #include<fstream> | ||||||
|  | #include "util/memory_manager.h" | ||||||
|  | #include "util/statistics.h" | ||||||
|  | #include "sat/dimacs.h" | ||||||
|  | #include "sat/sat_solver.h" | ||||||
|  | #include "sat/sat_drat.h" | ||||||
|  | #include "smt/smt_solver.h" | ||||||
|  | #include "shell/drat_frontend.h" | ||||||
|  | #include "parsers/smt2/smt2parser.h" | ||||||
|  | #include "cmd_context/cmd_context.h" | ||||||
|  | 
 | ||||||
|  | class smt_checker { | ||||||
|  |     ast_manager& m; | ||||||
|  |     expr_ref_vector const& m_b2e; | ||||||
|  |     expr_ref_vector m_fresh_exprs; | ||||||
|  |     expr_ref_vector m_core; | ||||||
|  |     params_ref m_params; | ||||||
|  |     scoped_ptr<solver> m_solver; | ||||||
|  | 
 | ||||||
|  |     expr* fresh(expr* e) { | ||||||
|  |         unsigned i = e->get_id(); | ||||||
|  |         m_fresh_exprs.reserve(i + 1); | ||||||
|  |         expr* r = m_fresh_exprs.get(i); | ||||||
|  |         if (!r) { | ||||||
|  |             r = m.mk_fresh_const("sk", m.get_sort(e)); | ||||||
|  |             m_fresh_exprs[i] = r; | ||||||
|  |         } | ||||||
|  |         return r; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     expr_ref define(expr* e, unsigned depth) { | ||||||
|  |         expr_ref r(fresh(e), m); | ||||||
|  |         m_core.push_back(m.mk_eq(r, e)); | ||||||
|  |         if (depth == 0) | ||||||
|  |             return r; | ||||||
|  |         r = e; | ||||||
|  |         if (is_app(e)) { | ||||||
|  |             expr_ref_vector args(m); | ||||||
|  |             for (expr* arg : *to_app(e))  | ||||||
|  |                 args.push_back(define(arg, depth - 1)); | ||||||
|  |             r = m.mk_app(to_app(e)->get_decl(), args.size(), args.c_ptr()); | ||||||
|  |         } | ||||||
|  |         return r; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void unfold1(sat::literal_vector const& lits) { | ||||||
|  |         m_core.reset(); | ||||||
|  |         for (sat::literal lit : lits) { | ||||||
|  |             expr* e = m_b2e[lit.var()]; | ||||||
|  |             expr_ref fml = define(e, 2); | ||||||
|  |             if (!lit.sign()) | ||||||
|  |                 fml = m.mk_not(fml); | ||||||
|  |             m_core.push_back(fml); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | public: | ||||||
|  |     smt_checker(expr_ref_vector const& b2e):  | ||||||
|  |         m(b2e.m()), m_b2e(b2e), m_fresh_exprs(m), m_core(m) { | ||||||
|  |         m_solver = mk_smt_solver(m, m_params, symbol()); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     void check_shallow(sat::literal_vector const& lits) { | ||||||
|  |         unfold1(lits); | ||||||
|  |         m_solver->push(); | ||||||
|  |         for (auto* c : m_core) | ||||||
|  |             m_solver->assert_expr(c); | ||||||
|  |         lbool is_sat = m_solver->check_sat(); | ||||||
|  |         m_solver->pop(1); | ||||||
|  |         if (is_sat == l_true) { | ||||||
|  |             std::cout << "did not verify: " << lits << "\n" << m_core << "\n"; | ||||||
|  |             for (sat::literal lit : lits) { | ||||||
|  |                 expr_ref e(m_b2e[lit.var()], m); | ||||||
|  |                 if (lit.sign()) | ||||||
|  |                     e = m.mk_not(e); | ||||||
|  |                 std::cout << e << " ";                 | ||||||
|  |             } | ||||||
|  |             std::cout << "\n"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void verify_smt(char const* drat_file, char const* smt_file) { | ||||||
|  |     cmd_context ctx; | ||||||
|  |     ctx.set_ignore_check(true); | ||||||
|  |     ctx.set_regular_stream(std::cerr); | ||||||
|  |     ctx.set_solver_factory(mk_smt_strategic_solver_factory());     | ||||||
|  |     if (smt_file) { | ||||||
|  |         std::ifstream smt_in(smt_file); | ||||||
|  |         if (!parse_smt2_commands(ctx, smt_in)) {             | ||||||
|  |             std::cerr << "could not read file " << smt_file << "\n"; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     std::ifstream ins(drat_file); | ||||||
|  |     dimacs::drat_parser drat(ins, std::cerr); | ||||||
|  |     std::function<int(char const* read_theory)> read_theory = [&](char const* r) { | ||||||
|  |         if (strcmp(r, "euf") == 0) | ||||||
|  |             return 0; | ||||||
|  |         return ctx.m().mk_family_id(symbol(r)); | ||||||
|  |     }; | ||||||
|  |     drat.set_read_theory(read_theory); | ||||||
|  |     params_ref p; | ||||||
|  |     reslimit lim; | ||||||
|  |     p.set_bool("drat.check_unsat", true); | ||||||
|  |     sat::solver solver(p, lim); | ||||||
|  |     sat::drat drat_checker(solver); | ||||||
|  |     drat_checker.updt_config(); | ||||||
|  | 
 | ||||||
|  |     expr_ref_vector bool_var2expr(ctx.m()); | ||||||
|  |     expr_ref_vector exprs(ctx.m()), args(ctx.m()); | ||||||
|  |     func_decl* f = nullptr; | ||||||
|  |     ptr_vector<sort> sorts; | ||||||
|  | 
 | ||||||
|  |     smt_checker checker(bool_var2expr); | ||||||
|  | 
 | ||||||
|  |     auto check_smt = [&](dimacs::drat_record const& r) { | ||||||
|  |         auto const& st = r.m_status; | ||||||
|  |         if (st.is_input()) | ||||||
|  |             ; | ||||||
|  |         else if (st.is_sat() && st.is_asserted()) { | ||||||
|  |             std::cout << "Tseitin tautology " << r; | ||||||
|  |             checker.check_shallow(r.m_lits); | ||||||
|  |         } | ||||||
|  |         else if (st.is_sat()) | ||||||
|  |             ; | ||||||
|  |         else if (st.is_deleted()) | ||||||
|  |             ; | ||||||
|  |         else { | ||||||
|  |             std::cout << "check smt " << r; | ||||||
|  |             checker.check_shallow(r.m_lits); | ||||||
|  |             // TBD: shallow check may fail because it doesn't include
 | ||||||
|  |             // all RUP units, whish are sometimes required.
 | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     for (auto const& r : drat) { | ||||||
|  |         std::cout << r; | ||||||
|  |         std::cout.flush(); | ||||||
|  |         switch (r.m_tag) { | ||||||
|  |         case dimacs::drat_record::tag_t::is_clause: | ||||||
|  |             for (sat::literal lit : r.m_lits) | ||||||
|  |                 while (lit.var() >= solver.num_vars()) | ||||||
|  |                     solver.mk_var(true); | ||||||
|  |             drat_checker.add(r.m_lits, r.m_status); | ||||||
|  |             check_smt(r); | ||||||
|  |             break; | ||||||
|  |         case dimacs::drat_record::tag_t::is_node: | ||||||
|  |             args.reset(); | ||||||
|  |             sorts.reset(); | ||||||
|  |             for (auto n : r.m_args) { | ||||||
|  |                 args.push_back(exprs.get(n)); | ||||||
|  |                 sorts.push_back(ctx.m().get_sort(args.back())); | ||||||
|  |             } | ||||||
|  |             if (r.m_name[0] == '(') { | ||||||
|  |                 std::cout << "parsing sexprs is TBD\n"; | ||||||
|  |                 exit(0); | ||||||
|  |             } | ||||||
|  |             f = ctx.find_func_decl(symbol(r.m_name.c_str()), 0, nullptr, args.size(), sorts.c_ptr(), nullptr); | ||||||
|  |             if (!f) { | ||||||
|  |                 std::cout << "could not find function\n"; | ||||||
|  |                 exit(0); | ||||||
|  |             } | ||||||
|  |             exprs.reserve(r.m_node_id+1); | ||||||
|  |             exprs.set(r.m_node_id, ctx.m().mk_app(f, args.size(), args.c_ptr())); | ||||||
|  |             break; | ||||||
|  |         case dimacs::drat_record::is_bool_def: | ||||||
|  |             bool_var2expr.reserve(r.m_node_id+1); | ||||||
|  |             bool_var2expr.set(r.m_node_id, exprs.get(r.m_args[0])); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             UNREACHABLE(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     statistics st; | ||||||
|  |     drat_checker.collect_statistics(st); | ||||||
|  |     std::cout << st << "\n"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | unsigned read_drat(char const* drat_file, char const* problem_file) { | ||||||
|  |     if (!problem_file) { | ||||||
|  |         std::cerr << "No smt2 file provided to checker\n"; | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     verify_smt(drat_file, problem_file); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										8
									
								
								src/shell/drat_frontend.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/shell/drat_frontend.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | --*/ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | unsigned read_drat(char const * drat_file, char const* problem_file); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -38,15 +38,17 @@ Revision History: | ||||||
| #include "util/env_params.h" | #include "util/env_params.h" | ||||||
| #include "util/file_path.h" | #include "util/file_path.h" | ||||||
| #include "shell/lp_frontend.h" | #include "shell/lp_frontend.h" | ||||||
|  | #include "shell/drat_frontend.h" | ||||||
| 
 | 
 | ||||||
| #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) | #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) | ||||||
| #include <crtdbg.h> | #include <crtdbg.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS } input_kind; | typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; | ||||||
| 
 | 
 | ||||||
| static std::string  g_aux_input_file; | static std::string  g_aux_input_file; | ||||||
| static char const * g_input_file          = nullptr; | static char const * g_input_file          = nullptr; | ||||||
|  | static char const * g_drat_input_file     = nullptr; | ||||||
| static bool         g_standard_input      = false; | static bool         g_standard_input      = false; | ||||||
| static input_kind   g_input_kind          = IN_UNSPECIFIED; | static input_kind   g_input_kind          = IN_UNSPECIFIED; | ||||||
| bool                g_display_statistics  = false; | bool                g_display_statistics  = false; | ||||||
|  | @ -301,13 +303,15 @@ static void parse_cmd_line_args(int argc, char ** argv) { | ||||||
|             gparams::set(key, value); |             gparams::set(key, value); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             if (g_input_file) { |             if (get_extension(arg) && strcmp(get_extension(arg), "drat") == 0) { | ||||||
|  |                 g_input_kind = IN_DRAT; | ||||||
|  |                 g_drat_input_file = arg; | ||||||
|  |             } | ||||||
|  |             else if (g_input_file)  | ||||||
|                 warning_msg("input file was already specified."); |                 warning_msg("input file was already specified."); | ||||||
|             } |             else  | ||||||
|             else { |  | ||||||
|                 g_input_file = arg; |                 g_input_file = arg; | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|         i++; |         i++; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -388,6 +392,9 @@ int STD_CALL main(int argc, char ** argv) { | ||||||
|         case IN_MPS: |         case IN_MPS: | ||||||
|             return_value = read_mps_file(g_input_file); |             return_value = read_mps_file(g_input_file); | ||||||
|             break; |             break; | ||||||
|  |         case IN_DRAT: | ||||||
|  |             return_value = read_drat(g_drat_input_file, g_input_file); | ||||||
|  |             break; | ||||||
|         default: |         default: | ||||||
|             UNREACHABLE(); |             UNREACHABLE(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| 
 |  | ||||||
| /*++
 | /*++
 | ||||||
| Copyright (c) 2015 Microsoft Corporation | Copyright (c) 2015 Microsoft Corporation | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -68,7 +68,6 @@ z3_add_component(smt | ||||||
|     theory_dl.cpp |     theory_dl.cpp | ||||||
|     theory_dummy.cpp |     theory_dummy.cpp | ||||||
|     theory_fpa.cpp |     theory_fpa.cpp | ||||||
|     theory_jobscheduler.cpp |  | ||||||
|     theory_lra.cpp |     theory_lra.cpp | ||||||
|     theory_opt.cpp |     theory_opt.cpp | ||||||
|     theory_pb.cpp |     theory_pb.cpp | ||||||
|  |  | ||||||
|  | @ -37,7 +37,6 @@ Revision History: | ||||||
| #include "smt/theory_pb.h" | #include "smt/theory_pb.h" | ||||||
| #include "smt/theory_fpa.h" | #include "smt/theory_fpa.h" | ||||||
| #include "smt/theory_str.h" | #include "smt/theory_str.h" | ||||||
| #include "smt/theory_jobscheduler.h" |  | ||||||
| 
 | 
 | ||||||
| namespace smt { | namespace smt { | ||||||
| 
 | 
 | ||||||
|  | @ -123,8 +122,6 @@ namespace smt { | ||||||
|             setup_UFLRA(); |             setup_UFLRA(); | ||||||
|         else if (m_logic == "LRA") |         else if (m_logic == "LRA") | ||||||
|             setup_LRA(); |             setup_LRA(); | ||||||
|         else if (m_logic == "CSP") |  | ||||||
|             setup_CSP(); |  | ||||||
|         else if (m_logic == "QF_FP") |         else if (m_logic == "QF_FP") | ||||||
|             setup_QF_FP(); |             setup_QF_FP(); | ||||||
|         else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") |         else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") | ||||||
|  | @ -203,8 +200,6 @@ namespace smt { | ||||||
|                 setup_QF_DT(); |                 setup_QF_DT(); | ||||||
|             else if (m_logic == "LRA") |             else if (m_logic == "LRA") | ||||||
|                 setup_LRA(); |                 setup_LRA(); | ||||||
|             else if (m_logic == "CSP") |  | ||||||
|                 setup_CSP(); |  | ||||||
|             else  |             else  | ||||||
|                 setup_unknown(st); |                 setup_unknown(st); | ||||||
|         } |         } | ||||||
|  | @ -932,11 +927,6 @@ namespace smt { | ||||||
|         m_context.register_plugin(alloc(smt::theory_seq, m_context)); |         m_context.register_plugin(alloc(smt::theory_seq, m_context)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void setup::setup_CSP() { |  | ||||||
|         setup_unknown(); |  | ||||||
|         m_context.register_plugin(alloc(smt::theory_jobscheduler, m_context)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void setup::setup_special_relations() { |     void setup::setup_special_relations() { | ||||||
|         m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager)); |         m_context.register_plugin(alloc(smt::theory_special_relations, m_context, m_manager)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -1,247 +0,0 @@ | ||||||
| /*++
 |  | ||||||
| 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_finite_capacity_end;    |  | ||||||
|             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_finite_capacity_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
 |  | ||||||
|             enode*                m_resource; |  | ||||||
|             enode*                m_makespan; |  | ||||||
|             res_info(): 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(context& ctx); |  | ||||||
| 
 |  | ||||||
|         ~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;  |  | ||||||
| 
 |  | ||||||
|         res_info& ensure_resource(unsigned r); |  | ||||||
|         job_info& ensure_job(unsigned j); |  | ||||||
| 
 |  | ||||||
|     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; |  | ||||||
|         bool job_has_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; |  | ||||||
| 
 |  | ||||||
|     };     |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
|  | @ -120,7 +120,9 @@ static void test3() { | ||||||
|     std::cout << g << "\n"; |     std::cout << g << "\n"; | ||||||
|     SASSERT(g.inconsistent()); |     SASSERT(g.inconsistent()); | ||||||
|     ptr_vector<int> js; |     ptr_vector<int> js; | ||||||
|  |     g.begin_explain(); | ||||||
|     g.explain<int>(js); |     g.explain<int>(js); | ||||||
|  |     g.end_explain(); | ||||||
|     for (int* j : js) { |     for (int* j : js) { | ||||||
|         std::cout << "conflict: " << *j << "\n"; |         std::cout << "conflict: " << *j << "\n"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -18,6 +18,9 @@ Revision History: | ||||||
| --*/ | --*/ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #if 0 | ||||||
|  | -- unused | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|    Add element \c elem to the list headed by \c head. |    Add element \c elem to the list headed by \c head. | ||||||
|    NextProc and PrevProc must have the methods: |    NextProc and PrevProc must have the methods: | ||||||
|  | @ -131,5 +134,52 @@ bool dlist_check_invariant(T * head, NextProc const & next = NextProc(), PrevPro | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | template<typename T> | ||||||
|  | class dll_base { | ||||||
|  |     T* m_next { nullptr }; | ||||||
|  |     T* m_prev { nullptr }; | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     T* prev() { return m_prev; } | ||||||
|  |     T* next() { return m_next; } | ||||||
|  | 
 | ||||||
|  |     void init(T* t) { | ||||||
|  |         m_next = t; | ||||||
|  |         m_prev = t; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     static void remove_from(T*& list, T* elem) { | ||||||
|  |         if (list->m_next == list) { | ||||||
|  |             SASSERT(elem == list); | ||||||
|  |             list = nullptr; | ||||||
|  |             return; | ||||||
|  |         }             | ||||||
|  |         if (list == elem) | ||||||
|  |             list = elem->m_next; | ||||||
|  |         auto* next = elem->m_next; | ||||||
|  |         auto* prev = elem->m_prev; | ||||||
|  |         prev->m_next = next; | ||||||
|  |         next->m_prev = prev;         | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static void push_to_front(T*& list, T* elem) { | ||||||
|  |         if (!list) { | ||||||
|  |             list = elem; | ||||||
|  |             elem->m_next = elem; | ||||||
|  |             elem->m_prev = elem;             | ||||||
|  |         } | ||||||
|  |         else if (list != elem) { | ||||||
|  |             remove_from(list, elem); | ||||||
|  |             list->m_prev->m_next = elem; | ||||||
|  |             elem->m_prev = list->m_prev; | ||||||
|  |             elem->m_next = list; | ||||||
|  |             list->m_prev = elem; | ||||||
|  |             list = elem; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								src/util/scoped_limit_trail.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/util/scoped_limit_trail.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | 
 | ||||||
|  | #include "util/vector.h" | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | class scoped_limit_trail { | ||||||
|  |     unsigned_vector m_lim; | ||||||
|  |     unsigned m_scopes{ 0 }; | ||||||
|  |     unsigned m_last{ 0 }; | ||||||
|  |  public: | ||||||
|  |      | ||||||
|  |     void push(unsigned n) {  | ||||||
|  |         if (m_last == n) | ||||||
|  |             m_scopes++;  | ||||||
|  |         else { | ||||||
|  |             for (; m_scopes > 0; --m_scopes) | ||||||
|  |                 m_lim.push_back(m_last); | ||||||
|  |             m_last = n; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     unsigned pop(unsigned n) { | ||||||
|  |         SASSERT(n > 0); | ||||||
|  |         SASSERT(m_scopes + m_lim.size() >= n); | ||||||
|  |         if (n <= m_scopes) { | ||||||
|  |             m_scopes -= n; | ||||||
|  |             return m_last; | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             n -= m_scopes; | ||||||
|  |             m_scopes = 0; | ||||||
|  |             m_last = m_lim[m_lim.size() - n]; | ||||||
|  |             m_lim.shrink(m_lim.size() - n); | ||||||
|  |             return m_last; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | @ -99,7 +99,7 @@ unsigned get_max_len(ptr_buffer<char> & keys) { | ||||||
|     return max; |     return max; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void statistics::display_smt2(std::ostream & out) const {    | std::ostream& statistics::display_smt2(std::ostream & out) const {    | ||||||
| #define INIT_DISPLAY()                                  \ | #define INIT_DISPLAY()                                  \ | ||||||
|     key2val m_u;                                        \ |     key2val m_u;                                        \ | ||||||
|     key2dval m_d;                                       \ |     key2dval m_d;                                       \ | ||||||
|  | @ -140,9 +140,10 @@ void statistics::display_smt2(std::ostream & out) const { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     out << ")\n"; |     out << ")\n"; | ||||||
|  |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void statistics::display(std::ostream & out) const { | std::ostream& statistics::display(std::ostream & out) const { | ||||||
|     INIT_DISPLAY(); |     INIT_DISPLAY(); | ||||||
| 
 | 
 | ||||||
| #undef DISPLAY_KEY | #undef DISPLAY_KEY | ||||||
|  | @ -169,6 +170,7 @@ void statistics::display(std::ostream & out) const { | ||||||
|             out << " " << std::fixed << std::setprecision(2) << d_val << "\n"; |             out << " " << std::fixed << std::setprecision(2) << d_val << "\n"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     return out; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<typename M> | template<typename M> | ||||||
|  |  | ||||||
|  | @ -32,8 +32,8 @@ public: | ||||||
|     void reset(); |     void reset(); | ||||||
|     void update(char const * key, unsigned inc); |     void update(char const * key, unsigned inc); | ||||||
|     void update(char const * key, double inc); |     void update(char const * key, double inc); | ||||||
|     void display(std::ostream & out) const; |     std::ostream& display(std::ostream & out) const; | ||||||
|     void display_smt2(std::ostream & out) const; |     std::ostream& display_smt2(std::ostream & out) const; | ||||||
|     void display_internal(std::ostream & out) const; |     void display_internal(std::ostream & out) const; | ||||||
|     unsigned size() const; |     unsigned size() const; | ||||||
|     bool is_uint(unsigned idx) const; |     bool is_uint(unsigned idx) const; | ||||||
|  | @ -42,6 +42,8 @@ public: | ||||||
|     double get_double_value(unsigned idx) const; |     double get_double_value(unsigned idx) const; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | inline std::ostream& operator<<(std::ostream& out, statistics const& st) { return st.display(out); } | ||||||
|  | 
 | ||||||
| void get_memory_statistics(statistics& st); | void get_memory_statistics(statistics& st); | ||||||
| void get_rlimit_statistics(reslimit& l, statistics& st); | void get_rlimit_statistics(reslimit& l, statistics& st); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -36,10 +36,10 @@ private: | ||||||
|     _trail_stack m_stack; |     _trail_stack m_stack; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template<typename Ctx = union_find_default_ctx> | template<typename Ctx = union_find_default_ctx, typename StackCtx = Ctx> | ||||||
| class union_find { | class union_find { | ||||||
|     Ctx &                         m_ctx; |     Ctx &                         m_ctx; | ||||||
|     trail_stack<Ctx> &            m_trail_stack; |     trail_stack<StackCtx> &       m_trail_stack; | ||||||
|     svector<unsigned>             m_find; |     svector<unsigned>             m_find; | ||||||
|     svector<unsigned>             m_size; |     svector<unsigned>             m_size; | ||||||
|     svector<unsigned>             m_next; |     svector<unsigned>             m_next; | ||||||
|  | @ -47,12 +47,12 @@ class union_find { | ||||||
|     class mk_var_trail; |     class mk_var_trail; | ||||||
|     friend class mk_var_trail; |     friend class mk_var_trail; | ||||||
| 
 | 
 | ||||||
|     class mk_var_trail : public trail<Ctx> { |     class mk_var_trail : public trail<StackCtx> { | ||||||
|         union_find & m_owner; |         union_find & m_owner; | ||||||
|     public: |     public: | ||||||
|         mk_var_trail(union_find & o):m_owner(o) {} |         mk_var_trail(union_find & o):m_owner(o) {} | ||||||
|         ~mk_var_trail() override {} |         ~mk_var_trail() override {} | ||||||
|         void undo(Ctx & ctx) override { |         void undo(StackCtx& ctx) override { | ||||||
|             m_owner.m_find.pop_back(); |             m_owner.m_find.pop_back(); | ||||||
|             m_owner.m_size.pop_back(); |             m_owner.m_size.pop_back(); | ||||||
|             m_owner.m_next.pop_back(); |             m_owner.m_next.pop_back(); | ||||||
|  | @ -64,13 +64,13 @@ class union_find { | ||||||
|     class merge_trail; |     class merge_trail; | ||||||
|     friend class merge_trail; |     friend class merge_trail; | ||||||
| 
 | 
 | ||||||
|     class merge_trail : public trail<Ctx> { |     class merge_trail : public trail<StackCtx> { | ||||||
|         union_find & m_owner; |         union_find & m_owner; | ||||||
|         unsigned     m_r1; |         unsigned     m_r1; | ||||||
|     public: |     public: | ||||||
|         merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {} |         merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {} | ||||||
|         ~merge_trail() override {} |         ~merge_trail() override {} | ||||||
|         void undo(Ctx & ctx) override { m_owner.unmerge(m_r1); } |         void undo(StackCtx& ctx) override { m_owner.unmerge(m_r1); } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     void unmerge(unsigned r1) { |     void unmerge(unsigned r1) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue