mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	Spacer engine for HORN logic
The algorithms implemented in the engine are described in the following papers Anvesh Komuravelli, Nikolaj Bjørner, Arie Gurfinkel, Kenneth L. McMillan: Compositional Verification of Procedural Programs using Horn Clauses over Integers and Arrays. FMCAD 2015: 89-96 Nikolaj Bjørner, Arie Gurfinkel: Property Directed Polyhedral Abstraction. VMCAI 2015: 263-281 Anvesh Komuravelli, Arie Gurfinkel, Sagar Chaki: SMT-Based Model Checking for Recursive Programs. CAV 2014: 17-34
This commit is contained in:
		
							parent
							
								
									9f9dc5e19f
								
							
						
					
					
						commit
						5b9bf74787
					
				
					 54 changed files with 18050 additions and 3 deletions
				
			
		|  | @ -65,12 +65,13 @@ def init_project_def(): | ||||||
|     add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') |     add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') | ||||||
|     add_lib('rel', ['muz', 'transforms'], 'muz/rel') |     add_lib('rel', ['muz', 'transforms'], 'muz/rel') | ||||||
|     add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'core_tactics', 'smt_tactic'], 'muz/pdr') |     add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'core_tactics', 'smt_tactic'], 'muz/pdr') | ||||||
|  |     add_lib('spacer', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/spacer') | ||||||
|     add_lib('clp', ['muz', 'transforms'], 'muz/clp') |     add_lib('clp', ['muz', 'transforms'], 'muz/clp') | ||||||
|     add_lib('tab', ['muz', 'transforms'], 'muz/tab') |     add_lib('tab', ['muz', 'transforms'], 'muz/tab') | ||||||
|     add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') |     add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') | ||||||
|     add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') |     add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') | ||||||
|     add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') |     add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') | ||||||
|     add_lib('fp',  ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf', 'ddnf'], 'muz/fp') |     add_lib('fp',  ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf', 'ddnf', 'spacer'], 'muz/fp') | ||||||
|     add_lib('nlsat_smt_tactic', ['nlsat_tactic', 'smt_tactic'], 'tactic/nlsat_smt') |     add_lib('nlsat_smt_tactic', ['nlsat_tactic', 'smt_tactic'], 'tactic/nlsat_smt') | ||||||
|     add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') |     add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') | ||||||
|     add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') |     add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') | ||||||
|  | @ -79,7 +80,7 @@ def init_project_def(): | ||||||
|     add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp',  'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') |     add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp',  'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') | ||||||
|     add_lib('smtparser', ['portfolio'], 'parsers/smt') |     add_lib('smtparser', ['portfolio'], 'parsers/smt') | ||||||
|     add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic', 'sat_solver'], 'opt') |     add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic', 'sat_solver'], 'opt') | ||||||
|     API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_interp.h', 'z3_fpa.h'] |     API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_interp.h', 'z3_fpa.h', 'z3_spacer.h'] | ||||||
|     add_lib('api', ['portfolio', 'smtparser', 'realclosure', 'interp', 'opt'], |     add_lib('api', ['portfolio', 'smtparser', 'realclosure', 'interp', 'opt'], | ||||||
|             includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) |             includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) | ||||||
|     add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') |     add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') | ||||||
|  |  | ||||||
|  | @ -92,6 +92,7 @@ add_subdirectory(muz/tab) | ||||||
| add_subdirectory(muz/bmc) | add_subdirectory(muz/bmc) | ||||||
| add_subdirectory(muz/ddnf) | add_subdirectory(muz/ddnf) | ||||||
| add_subdirectory(muz/duality) | add_subdirectory(muz/duality) | ||||||
|  | add_subdirectory(muz/spacer) | ||||||
| add_subdirectory(muz/fp) | add_subdirectory(muz/fp) | ||||||
| add_subdirectory(tactic/nlsat_smt) | add_subdirectory(tactic/nlsat_smt) | ||||||
| add_subdirectory(tactic/ufbv) | add_subdirectory(tactic/ufbv) | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ namespace datalog { | ||||||
|     enum DL_ENGINE { |     enum DL_ENGINE { | ||||||
|         DATALOG_ENGINE, |         DATALOG_ENGINE, | ||||||
|         PDR_ENGINE, |         PDR_ENGINE, | ||||||
|  |         SPACER_ENGINE, | ||||||
|         QPDR_ENGINE, |         QPDR_ENGINE, | ||||||
|         BMC_ENGINE, |         BMC_ENGINE, | ||||||
|         QBMC_ENGINE, |         QBMC_ENGINE, | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ def_module_params('fixedpoint', | ||||||
|                   export=True, |                   export=True, | ||||||
|                   params=(('timeout', UINT, UINT_MAX, 'set timeout'), |                   params=(('timeout', UINT, UINT_MAX, 'set timeout'), | ||||||
|                           ('engine', SYMBOL, 'auto-config',  |                           ('engine', SYMBOL, 'auto-config',  | ||||||
|                            'Select: auto-config, datalog, duality, pdr, bmc'), |                            'Select: auto-config, datalog, duality, pdr, bmc, spacer'), | ||||||
| 			  ('datalog.default_table', SYMBOL, 'sparse',  | 			  ('datalog.default_table', SYMBOL, 'sparse',  | ||||||
|                            'default table implementation: sparse, hashtable, bitvector, interval'), |                            'default table implementation: sparse, hashtable, bitvector, interval'), | ||||||
|                           ('datalog.default_relation', SYMBOL, 'pentagon',  |                           ('datalog.default_relation', SYMBOL, 'pentagon',  | ||||||
|  | @ -54,6 +54,8 @@ def_module_params('fixedpoint', | ||||||
|                            "if true, finite_product_relation will attempt to avoid creating " + |                            "if true, finite_product_relation will attempt to avoid creating " + | ||||||
|                            "inner relation with empty signature by putting in half of the " + |                            "inner relation with empty signature by putting in half of the " + | ||||||
|                            "table columns, if it would have been empty otherwise"), |                            "table columns, if it would have been empty otherwise"), | ||||||
|  |                           ('datalog.subsumption', BOOL, True, | ||||||
|  |                            "if true, removes/filters predicates with total transitions"), | ||||||
| 	                  ('duality.full_expand', BOOL, False, 'Fully expand derivation trees'), | 	                  ('duality.full_expand', BOOL, False, 'Fully expand derivation trees'), | ||||||
| 	                  ('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'), | 	                  ('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'), | ||||||
| 	                  ('duality.feasible_edges', BOOL, True,  | 	                  ('duality.feasible_edges', BOOL, True,  | ||||||
|  | @ -74,6 +76,8 @@ def_module_params('fixedpoint', | ||||||
|                           ('pdr.flexible_trace', BOOL, False,  |                           ('pdr.flexible_trace', BOOL, False,  | ||||||
|                            "allow PDR generate long counter-examples " + |                            "allow PDR generate long counter-examples " + | ||||||
|                            "by extending candidate trace within search area"), |                            "by extending candidate trace within search area"), | ||||||
|  |                           ('pdr.flexible_trace_depth', UINT, UINT_MAX,  | ||||||
|  |                            'Controls the depth (below the current level) at which flexible trace can be applied'), | ||||||
|                           ('pdr.use_model_generalizer', BOOL, False,  |                           ('pdr.use_model_generalizer', BOOL, False,  | ||||||
|                            "use model for backwards propagation (instead of symbolic simulation)"), |                            "use model for backwards propagation (instead of symbolic simulation)"), | ||||||
|                           ('pdr.validate_result', BOOL, False,  |                           ('pdr.validate_result', BOOL, False,  | ||||||
|  | @ -138,13 +142,65 @@ def_module_params('fixedpoint', | ||||||
|                           ('xform.slice', BOOL, True, "simplify clause set using slicing"), |                           ('xform.slice', BOOL, True, "simplify clause set using slicing"), | ||||||
| 	                  ('xform.karr',  BOOL, False,  | 	                  ('xform.karr',  BOOL, False,  | ||||||
|                            "Add linear invariants to clauses using Karr's method"), |                            "Add linear invariants to clauses using Karr's method"), | ||||||
|  |                           ('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"), | ||||||
|  |                           ('xform.transform_arrays',  BOOL, False,  | ||||||
|  |                            "Rewrites arrays equalities and applies select over store"), | ||||||
|  |                           ('xform.instantiate_arrays',  BOOL, False,  | ||||||
|  |                            "Transforms P(a) into P(i, a[i] a)"), | ||||||
|  |                           ('xform.instantiate_arrays.enforce',  BOOL, False,  | ||||||
|  |                            "Transforms P(a) into P(i, a[i]), discards a from predicate"), | ||||||
|  |                           ('xform.instantiate_arrays.nb_quantifier',  UINT, 1,  | ||||||
|  |                            "Gives the number of quantifiers per array"), | ||||||
|  |                           ('xform.instantiate_arrays.slice_technique',  SYMBOL, "no-slicing",  | ||||||
|  |                            "<no-slicing>=> GetId(i) = i, <smash> => GetId(i) = true"), | ||||||
| 	                  ('xform.quantify_arrays', BOOL, False,  | 	                  ('xform.quantify_arrays', BOOL, False,  | ||||||
|                            "create quantified Horn clauses from clauses with arrays"), |                            "create quantified Horn clauses from clauses with arrays"), | ||||||
| 	                  ('xform.instantiate_quantifiers', BOOL, False,  | 	                  ('xform.instantiate_quantifiers', BOOL, False,  | ||||||
|                            "instantiate quantified Horn clauses using E-matching heuristic"), |                            "instantiate quantified Horn clauses using E-matching heuristic"), | ||||||
|                           ('xform.coalesce_rules', BOOL, False, "coalesce rules"), |                           ('xform.coalesce_rules', BOOL, False, "coalesce rules"), | ||||||
|  |                           ('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"), | ||||||
|  |                           ('xform.subsumption_checker', BOOL, True, "Enable subsumption checker (no support for model conversion)"), | ||||||
|                           ('xform.coi', BOOL, True, "use cone of influence simplificaiton"), |                           ('xform.coi', BOOL, True, "use cone of influence simplificaiton"), | ||||||
| 			  ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), | 			  ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), | ||||||
|  |                           ('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse)'), | ||||||
|  |                           ('spacer.eager_reach_check', BOOL, True, 'SPACER: eagerly check if a query is reachable using reachability facts of predecessors'), | ||||||
|  |                           ('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'), | ||||||
|  |                           ('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'), | ||||||
|  |                           ('spacer.init_reach_facts', BOOL, True, 'SPACER: initialize reachability facts with false'), | ||||||
|  |                           ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), | ||||||
|  |                           ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'),	 | ||||||
|  |                           ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"),  | ||||||
|  | 	                  ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), | ||||||
|  |                           ('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"), | ||||||
|  |                           ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), | ||||||
|  |                           ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), | ||||||
|  |                           ('spacer.reach_as_init', BOOL, True, "Extend initial rules with computed reachability facts"), | ||||||
|  |                           ('spacer.blast_term_ite', BOOL, True, "Expand non-Boolean ite-terms"), | ||||||
|  |                           ('spacer.nondet_tie_break', BOOL, False, "Break ties in obligation queue non-deterministicly"), | ||||||
|  |                           ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), | ||||||
|  |                           ('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"), | ||||||
|  |                           ('spacer.split_farkas_literals', BOOL, False, "Split Farkas literals"), | ||||||
|  |                           ('spacer.native_mbp', BOOL, False, "Use native mbp of Z3"), | ||||||
|  |                           ('spacer.eq_prop', BOOL, True, "Enable equality and bound propagation in arithmetic"), | ||||||
|  |                           ('spacer.weak_abs', BOOL, True, "Weak abstraction"), | ||||||
|  |                           ('spacer.restarts', BOOL, False, "Enable reseting obligation queue"), | ||||||
|  |                           ('spacer.restart_initial_threshold', UINT, 10, "Intial threshold for restarts"), | ||||||
|  |                           ('spacer.random_seed', UINT, 0, "Random seed to be used by SMT solver"), | ||||||
|  |                           ('spacer.ground_cti', BOOL, True, "Require CTI to be ground"), | ||||||
|  |                           ('spacer.vs.dump_benchmarks', BOOL, False, 'dump benchmarks in virtual solver'), | ||||||
|  |                           ('spacer.vs.dump_min_time', DOUBLE, 5.0, 'min time to dump benchmark'), | ||||||
|  |                           ('spacer.vs.recheck', BOOL, False, 're-check locally during benchmark dumping'), | ||||||
|  |                           ('spacer.mbqi', BOOL, True, 'use model-based quantifier instantiation'), | ||||||
|  |                           ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), | ||||||
|  |                           ('spacer.instantiate', BOOL, True, 'instantiate quantified lemmas'), | ||||||
|  |                           ('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'), | ||||||
|  |                           ('spacer.new_unsat_core', BOOL, True, 'use the new implementation of unsat-core-generation'), | ||||||
|  |                           ('spacer.minimize_unsat_core', BOOL, False, 'compute unsat-core by min-cut'),                           | ||||||
|  |                           ('spacer.farkas_optimized', BOOL, True, 'use the optimized farkas plugin, which performs gaussian elimination'),   | ||||||
|  |                           ('spacer.farkas_a_const', BOOL, True, 'if the unoptimized farkas plugin is used, use the constants from A while constructing unsat_cores'),      | ||||||
|  |                           ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), | ||||||
|  |                           ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), | ||||||
|  |                           ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints') | ||||||
|                           )) |                           )) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ z3_add_component(fp | ||||||
|     muz |     muz | ||||||
|     pdr |     pdr | ||||||
|     rel |     rel | ||||||
|  |     spacer | ||||||
|     tab |     tab | ||||||
|   TACTIC_HEADERS |   TACTIC_HEADERS | ||||||
|     horn_tactic.h |     horn_tactic.h | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ Revision History: | ||||||
| #include "muz/pdr/pdr_dl_interface.h" | #include "muz/pdr/pdr_dl_interface.h" | ||||||
| #include "muz/ddnf/ddnf.h" | #include "muz/ddnf/ddnf.h" | ||||||
| #include "muz/duality/duality_dl_interface.h" | #include "muz/duality/duality_dl_interface.h" | ||||||
|  | #include "muz/spacer/spacer_dl_interface.h" | ||||||
| 
 | 
 | ||||||
| namespace datalog { | namespace datalog { | ||||||
|     register_engine::register_engine(): m_ctx(0) {} |     register_engine::register_engine(): m_ctx(0) {} | ||||||
|  | @ -33,6 +34,8 @@ namespace datalog { | ||||||
|         case PDR_ENGINE: |         case PDR_ENGINE: | ||||||
|         case QPDR_ENGINE: |         case QPDR_ENGINE: | ||||||
|             return alloc(pdr::dl_interface, *m_ctx); |             return alloc(pdr::dl_interface, *m_ctx); | ||||||
|  |         case SPACER_ENGINE: | ||||||
|  |             return alloc(spacer::dl_interface, *m_ctx); | ||||||
|         case DATALOG_ENGINE: |         case DATALOG_ENGINE: | ||||||
|             return alloc(rel_context, *m_ctx); |             return alloc(rel_context, *m_ctx); | ||||||
|         case BMC_ENGINE: |         case BMC_ENGINE: | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								src/muz/spacer/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/muz/spacer/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | z3_add_component(spacer | ||||||
|  |   SOURCES | ||||||
|  |   spacer_legacy_mev.cpp | ||||||
|  |   spacer_legacy_frames.cpp | ||||||
|  |   spacer_context.cpp | ||||||
|  |   spacer_dl_interface.cpp | ||||||
|  |   spacer_farkas_learner.cpp | ||||||
|  |   spacer_generalizers.cpp | ||||||
|  |   spacer_manager.cpp | ||||||
|  |   spacer_marshal.cpp | ||||||
|  |   spacer_prop_solver.cpp | ||||||
|  |   spacer_smt_context_manager.cpp | ||||||
|  |   spacer_sym_mux.cpp | ||||||
|  |   spacer_util.cpp | ||||||
|  |   spacer_itp_solver.cpp | ||||||
|  |   spacer_virtual_solver.cpp | ||||||
|  |   spacer_legacy_mbp.cpp | ||||||
|  |   spacer_proof_utils.cpp | ||||||
|  |   spacer_unsat_core_learner.cpp | ||||||
|  |   spacer_unsat_core_plugin.cpp | ||||||
|  |   spacer_matrix.cpp | ||||||
|  |   spacer_min_cut.cpp | ||||||
|  |   spacer_antiunify.cpp | ||||||
|  |   spacer_mev_array.cpp | ||||||
|  |   spacer_qe_project.cpp | ||||||
|  |   COMPONENT_DEPENDENCIES | ||||||
|  |   arith_tactics | ||||||
|  |   core_tactics | ||||||
|  |   muz | ||||||
|  |   qe | ||||||
|  |   smt_tactic | ||||||
|  |   transforms | ||||||
|  |   ) | ||||||
							
								
								
									
										250
									
								
								src/muz/spacer/obj_equiv_class.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								src/muz/spacer/obj_equiv_class.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,250 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     obj_equiv_class.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |   "Equivalence class structure" for objs. Uses a union_find structure internally. | ||||||
|  |   Operations are : | ||||||
|  |   -Declare a new equivalence class with a single element | ||||||
|  |   -Merge two equivalence classes | ||||||
|  |   -Retrieve whether two elements are in the same equivalence class | ||||||
|  |   -Iterate on all the elements of the equivalence class of a given element | ||||||
|  |   -Iterate on all equivalence classes (and then within them) | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Julien Braine | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #ifndef OBJ_EQUIV_CLASS_H_ | ||||||
|  | #define OBJ_EQUIV_CLASS_H_ | ||||||
|  | 
 | ||||||
|  | #include "union_find.h" | ||||||
|  | #include "ast_util.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | //All functions naturally add their parameters to the union_find class
 | ||||||
|  | template<typename OBJ, typename Manager> | ||||||
|  | class obj_equiv_class { | ||||||
|  |     basic_union_find m_uf; | ||||||
|  |     obj_map<OBJ, unsigned> m_to_int; | ||||||
|  |     ref_vector<OBJ, Manager> m_to_obj; | ||||||
|  | 
 | ||||||
|  |     unsigned add_elem_impl(OBJ*o) { | ||||||
|  |         unsigned id = m_to_obj.size(); | ||||||
|  |         m_to_int.insert(o, id); | ||||||
|  |         m_to_obj.push_back(o); | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  |     unsigned add_if_not_there(OBJ*o) { | ||||||
|  |         unsigned id; | ||||||
|  |         if(!m_to_int.find(o, id)) { | ||||||
|  |             id = add_elem_impl(o); | ||||||
|  |         } | ||||||
|  |         return id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     class iterator; | ||||||
|  |     class equiv_iterator; | ||||||
|  |     friend class iterator; | ||||||
|  |     friend class equiv_iterator; | ||||||
|  | 
 | ||||||
|  |     obj_equiv_class(Manager& m) : m_to_obj(m) {} | ||||||
|  | 
 | ||||||
|  |     void add_elem(OBJ*o) { | ||||||
|  |         SASSERT(!m_to_int.find(o)); | ||||||
|  |         add_elem_impl(o); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     //Invalidates all iterators
 | ||||||
|  |     void merge(OBJ* a, OBJ* b) { | ||||||
|  |         unsigned v1 = add_if_not_there(a); | ||||||
|  |         unsigned v2 = add_if_not_there(b); | ||||||
|  |         unsigned tmp1 = m_uf.find(v1); | ||||||
|  |         unsigned tmp2 = m_uf.find(v2); | ||||||
|  |         m_uf.merge(tmp1, tmp2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void reset() { | ||||||
|  |         m_uf.reset(); | ||||||
|  |         m_to_int.reset(); | ||||||
|  |         m_to_obj.reset(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool are_equiv(OBJ*a, OBJ*b) { | ||||||
|  |         unsigned id1 = add_if_not_there(a); | ||||||
|  |         unsigned id2 = add_if_not_there(b); | ||||||
|  |         return m_uf.find(id1) == m_uf.find(id2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     class iterator { | ||||||
|  |         friend class obj_equiv_class; | ||||||
|  |     private : | ||||||
|  |         const obj_equiv_class& m_ouf; | ||||||
|  |         unsigned m_curr_id; | ||||||
|  |         bool m_first; | ||||||
|  |         iterator(const obj_equiv_class& uf, unsigned id, bool f) : | ||||||
|  |             m_ouf(uf), m_curr_id(id), m_first(f) {} | ||||||
|  |     public : | ||||||
|  |         OBJ*operator*() {return m_ouf.m_to_obj[m_curr_id];} | ||||||
|  | 
 | ||||||
|  |         iterator& operator++() { | ||||||
|  |             m_curr_id = m_ouf.m_uf.next(m_curr_id); | ||||||
|  |             m_first = false; | ||||||
|  |             return *this; | ||||||
|  |         } | ||||||
|  |         bool operator==(const iterator& o) { | ||||||
|  |             SASSERT(&m_ouf == &o.m_ouf); | ||||||
|  |             return  m_first == o.m_first && m_curr_id == o.m_curr_id; | ||||||
|  |         } | ||||||
|  |         bool operator!=(const iterator& o) {return !(*this == o);} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     iterator begin(OBJ*o) { | ||||||
|  |         unsigned id = add_if_not_there(o); | ||||||
|  |         return iterator(*this, id, true); | ||||||
|  |     } | ||||||
|  |     iterator end(OBJ*o) { | ||||||
|  |         unsigned id = add_if_not_there(o); | ||||||
|  |         return iterator(*this, id, false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     class eq_class { | ||||||
|  |     private : | ||||||
|  |         iterator m_begin; | ||||||
|  |         iterator m_end; | ||||||
|  |     public : | ||||||
|  |         eq_class(const iterator& a, const iterator& b) : m_begin(a), m_end(b) {} | ||||||
|  |         iterator begin() {return m_begin;} | ||||||
|  |         iterator end() {return m_end;} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class equiv_iterator { | ||||||
|  |         friend class obj_equiv_class; | ||||||
|  |     private : | ||||||
|  |         const obj_equiv_class& m_ouf; | ||||||
|  |         unsigned m_rootnb; | ||||||
|  |         equiv_iterator(const obj_equiv_class& uf, unsigned nb) : | ||||||
|  |             m_ouf(uf), m_rootnb(nb) { | ||||||
|  |             while(m_rootnb != m_ouf.m_to_obj.size() && | ||||||
|  |                   m_ouf.m_uf.is_root(m_rootnb) != true) | ||||||
|  |             { m_rootnb++; } | ||||||
|  |         } | ||||||
|  |     public : | ||||||
|  |         eq_class operator*() { | ||||||
|  |             return eq_class(iterator(m_ouf, m_rootnb, true), | ||||||
|  |                             iterator(m_ouf, m_rootnb, false)); | ||||||
|  |         } | ||||||
|  |         equiv_iterator& operator++() { | ||||||
|  |             do { | ||||||
|  |                 m_rootnb++; | ||||||
|  |             } while(m_rootnb != m_ouf.m_to_obj.size() && | ||||||
|  |                     m_ouf.m_uf.is_root(m_rootnb) != true); | ||||||
|  |             return *this; | ||||||
|  |         } | ||||||
|  |         bool operator==(const equiv_iterator& o) { | ||||||
|  |             SASSERT(&m_ouf == &o.m_ouf); | ||||||
|  |             return m_rootnb == o.m_rootnb; | ||||||
|  |         } | ||||||
|  |         bool operator!=(const equiv_iterator& o) {return !(*this == o);} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     equiv_iterator begin() {return equiv_iterator(*this, 0);} | ||||||
|  |     equiv_iterator end() {return equiv_iterator(*this, m_to_obj.size());} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef obj_equiv_class<expr, ast_manager> expr_equiv_class; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |    Factors input vector v into equivalence classes and the rest | ||||||
|  |  */ | ||||||
|  | inline void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { | ||||||
|  |     ast_manager &m = v.get_manager(); | ||||||
|  |     arith_util arith(m); | ||||||
|  |     expr *e1, *e2; | ||||||
|  | 
 | ||||||
|  |     flatten_and(v); | ||||||
|  |     unsigned j = 0; | ||||||
|  |     for (unsigned i = 0; i < v.size(); ++i) { | ||||||
|  |         if (m.is_eq(v.get(i), e1, e2)) { | ||||||
|  |             if (arith.is_zero(e1)) { | ||||||
|  |                 expr* t; | ||||||
|  |                 t = e1; e1 = e2; e2 = t; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // y + -1*x == 0
 | ||||||
|  |             if (arith.is_zero(e2) && arith.is_add(e1) && | ||||||
|  |                 to_app(e1)->get_num_args() == 2) { | ||||||
|  |                 expr *a0, *a1, *x; | ||||||
|  | 
 | ||||||
|  |                 a0 = to_app(e1)->get_arg(0); | ||||||
|  |                 a1 = to_app(e1)->get_arg(1); | ||||||
|  | 
 | ||||||
|  |                 if (arith.is_times_minus_one(a1, x)) { | ||||||
|  |                     e1 = a0; | ||||||
|  |                     e2 = x; | ||||||
|  |                 } | ||||||
|  |                 else if (arith.is_times_minus_one(a0, x)) { | ||||||
|  |                     e1 = a1; | ||||||
|  |                     e2 = x; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             equiv.merge(e1, e2); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             if (j < i) {v[j] = v.get(i);} | ||||||
|  |             j++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     v.shrink(j); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * converts equivalence classes to equalities | ||||||
|  |  */ | ||||||
|  | inline void equiv_to_expr(expr_equiv_class &equiv, expr_ref_vector &out) { | ||||||
|  |     ast_manager &m = out.get_manager(); | ||||||
|  |     for (auto eq_class : equiv) { | ||||||
|  |         expr *rep = nullptr; | ||||||
|  |         for (expr *elem : eq_class) { | ||||||
|  |             if (!m.is_value (elem)) { | ||||||
|  |                 rep = elem; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         SASSERT(rep); | ||||||
|  |         for (expr *elem : eq_class) { | ||||||
|  |             if (rep != elem) {out.push_back (m.mk_eq (rep, elem));} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * expands equivalence classes to all derivable equalities | ||||||
|  |  */ | ||||||
|  | inline bool equiv_to_expr_full(expr_equiv_class &equiv, expr_ref_vector &out) { | ||||||
|  |     ast_manager &m = out.get_manager(); | ||||||
|  |     bool dirty = false; | ||||||
|  |     for (auto eq_class : equiv) { | ||||||
|  |         for (auto a = eq_class.begin(), end = eq_class.end(); a != end; ++a) { | ||||||
|  |             expr_equiv_class::iterator b(a); | ||||||
|  |             for (++b; b != end; ++b) { | ||||||
|  |                 out.push_back(m.mk_eq(*a, *b)); | ||||||
|  |                 dirty = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return dirty; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										459
									
								
								src/muz/spacer/spacer_antiunify.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								src/muz/spacer/spacer_antiunify.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,459 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_antiunify.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |   Antiunification utilities | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Bernhard Gleiss | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include"spacer_antiunify.h" | ||||||
|  | #include"ast.h" | ||||||
|  | #include"rewriter.h" | ||||||
|  | #include"rewriter_def.h" | ||||||
|  | #include"arith_decl_plugin.h" | ||||||
|  | #include"ast_util.h" | ||||||
|  | #include"expr_abstract.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | // Abstracts numeric values by variables
 | ||||||
|  | struct var_abs_rewriter : public default_rewriter_cfg { | ||||||
|  |     ast_manager &m; | ||||||
|  |     arith_util m_util; | ||||||
|  |     ast_mark m_seen; | ||||||
|  |     ast_mark m_has_num; | ||||||
|  |     unsigned m_var_index; | ||||||
|  |     expr_ref_vector m_pinned; | ||||||
|  |     obj_map<expr, expr*>& m_substitution; | ||||||
|  |     ptr_vector<expr> m_stack; | ||||||
|  | 
 | ||||||
|  |     var_abs_rewriter (ast_manager &manager, obj_map<expr, expr*>& substitution, | ||||||
|  |                       unsigned k = 0) : | ||||||
|  |         m(manager), m_util(m), m_var_index(k), | ||||||
|  |         m_pinned(m), m_substitution(substitution) {} | ||||||
|  | 
 | ||||||
|  |     void reset(unsigned k = 0) { | ||||||
|  |         m_pinned.reset(); | ||||||
|  |         m_var_index = k; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool pre_visit(expr * t) { | ||||||
|  |         bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t)); | ||||||
|  |         // only unify if convex closure will not contain non-linear multiplication
 | ||||||
|  |         if (m_util.is_mul(t)) | ||||||
|  |         { | ||||||
|  |             bool contains_const_child = false; | ||||||
|  |             app* a = to_app(t); | ||||||
|  |             for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) { | ||||||
|  |                 if (m_util.is_numeral(a->get_arg(i))) { | ||||||
|  |                     contains_const_child = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (!contains_const_child) {r = false;} | ||||||
|  |         } | ||||||
|  |         if (r) {m_stack.push_back (t);} | ||||||
|  |         return r; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     br_status reduce_app (func_decl * f, unsigned num, expr * const * args, | ||||||
|  |                           expr_ref & result, proof_ref & result_pr) { | ||||||
|  |         expr *s; | ||||||
|  |         s = m_stack.back(); | ||||||
|  |         m_stack.pop_back(); | ||||||
|  |         if (is_app(s)) { | ||||||
|  |             app *a = to_app(s); | ||||||
|  |             for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) { | ||||||
|  |                 if (m_has_num.is_marked(a->get_arg(i))) { | ||||||
|  |                     m_has_num.mark(a,true); | ||||||
|  |                     return BR_FAILED; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return BR_FAILED; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool cache_all_results() const { return false; } | ||||||
|  |     bool cache_results() const { return false; } | ||||||
|  | 
 | ||||||
|  |     bool get_subst(expr * s, expr * & t, proof * & t_pr) { | ||||||
|  |         if (m_util.is_numeral(s)) { | ||||||
|  |             t = m.mk_var(m_var_index++, m.get_sort(s)); | ||||||
|  |             m_substitution.insert(t, s); | ||||||
|  |             m_pinned.push_back(t); | ||||||
|  |             m_has_num.mark(s, true); | ||||||
|  |             m_seen.mark(t, true); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | * construct m_g, which is a generalization of t, where every constant | ||||||
|  | * is replaced by a variable for any variable in m_g, remember the | ||||||
|  | * substitution to get back t and save it in m_substitutions | ||||||
|  | */ | ||||||
|  | anti_unifier::anti_unifier(expr* t, ast_manager& man) : m(man), m_pinned(m), m_g(m) | ||||||
|  | { | ||||||
|  |     m_pinned.push_back(t); | ||||||
|  | 
 | ||||||
|  |     obj_map<expr, expr*> substitution; | ||||||
|  | 
 | ||||||
|  |     var_abs_rewriter var_abs_cfg(m, substitution); | ||||||
|  |     rewriter_tpl<var_abs_rewriter> var_abs_rw (m, false, var_abs_cfg); | ||||||
|  |     var_abs_rw (t, m_g); | ||||||
|  | 
 | ||||||
|  |     m_substitutions.push_back(substitution); //TODO: refactor into vector, remove k
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* traverses m_g and t in parallel. if they only differ in constants
 | ||||||
|  |  * (i.e. m_g contains a variable, where t contains a constant), then | ||||||
|  |  * add the substitutions, which need to be applied to m_g to get t, to | ||||||
|  |  * m_substitutions. | ||||||
|  | */ | ||||||
|  | bool anti_unifier::add_term(expr* t) { | ||||||
|  |     m_pinned.push_back(t); | ||||||
|  | 
 | ||||||
|  |     ptr_vector<expr> todo; | ||||||
|  |     ptr_vector<expr> todo2; | ||||||
|  |     todo.push_back(m_g); | ||||||
|  |     todo2.push_back(t); | ||||||
|  | 
 | ||||||
|  |     ast_mark visited; | ||||||
|  | 
 | ||||||
|  |     arith_util util(m); | ||||||
|  | 
 | ||||||
|  |     obj_map<expr, expr*> substitution; | ||||||
|  | 
 | ||||||
|  |     while (!todo.empty()) { | ||||||
|  |         expr* current = todo.back(); | ||||||
|  |         todo.pop_back(); | ||||||
|  |         expr* current2 = todo2.back(); | ||||||
|  |         todo2.pop_back(); | ||||||
|  | 
 | ||||||
|  |         if (!visited.is_marked(current)) { | ||||||
|  |             visited.mark(current, true); | ||||||
|  | 
 | ||||||
|  |             if (is_var(current)) { | ||||||
|  |                 // TODO: for now we don't allow variables in the terms we want to antiunify
 | ||||||
|  |                 SASSERT(m_substitutions[0].contains(current)); | ||||||
|  |                 if (util.is_numeral(current2)) { | ||||||
|  |                     substitution.insert(current, current2); | ||||||
|  |                 } | ||||||
|  |                 else {return false;} | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 SASSERT(is_app(current)); | ||||||
|  | 
 | ||||||
|  |                 if (is_app(current2) && | ||||||
|  |                     to_app(current)->get_decl() == to_app(current2)->get_decl() && | ||||||
|  |                     to_app(current)->get_num_args() == to_app(current2)->get_num_args()) { | ||||||
|  |                     // TODO: what to do for numerals here? E.g. if we
 | ||||||
|  |                     // have 1 and 2, do they have the same decl or are
 | ||||||
|  |                     // the decls already different?
 | ||||||
|  |                     SASSERT (!util.is_numeral(current) || current == current2); | ||||||
|  |                     for (unsigned i = 0, num_args = to_app(current)->get_num_args(); | ||||||
|  |                          i < num_args; ++i) { | ||||||
|  |                         todo.push_back(to_app(current)->get_arg(i)); | ||||||
|  |                         todo2.push_back(to_app(current2)->get_arg(i)); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // we now know that the terms can be anti-unified, so add the cached substitution
 | ||||||
|  |     m_substitutions.push_back(substitution); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  | * returns m_g, where additionally any variable, which has only equal | ||||||
|  | * substitutions, is substituted with that substitution | ||||||
|  | */ | ||||||
|  | void anti_unifier::finalize() { | ||||||
|  |     ptr_vector<expr> todo; | ||||||
|  |     todo.push_back(m_g); | ||||||
|  | 
 | ||||||
|  |     ast_mark visited; | ||||||
|  | 
 | ||||||
|  |     obj_map<expr, expr*> generalization; | ||||||
|  | 
 | ||||||
|  |     arith_util util(m); | ||||||
|  | 
 | ||||||
|  |     // post-order traversel which ignores constants and handles them
 | ||||||
|  |     // directly when the enclosing term of the constant is handled
 | ||||||
|  |     while (!todo.empty()) { | ||||||
|  |         expr* current = todo.back(); | ||||||
|  |         SASSERT(is_app(current)); | ||||||
|  | 
 | ||||||
|  |         // if we haven't already visited current
 | ||||||
|  |         if (!visited.is_marked(current)) { | ||||||
|  |             bool existsUnvisitedParent = false; | ||||||
|  | 
 | ||||||
|  |             for (unsigned i = 0, sz = to_app(current)->get_num_args(); i < sz; ++i) { | ||||||
|  |                 expr* argument = to_app(current)->get_arg(i); | ||||||
|  | 
 | ||||||
|  |                 if (!is_var(argument)) { | ||||||
|  |                     SASSERT(is_app(argument)); | ||||||
|  |                     // if we haven't visited the current parent yet
 | ||||||
|  |                     if(!visited.is_marked(argument)) { | ||||||
|  |                         // add it to the stack
 | ||||||
|  |                         todo.push_back(argument); | ||||||
|  |                         existsUnvisitedParent = true; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // if we already visited all parents, we can visit current too
 | ||||||
|  |             if (!existsUnvisitedParent) { | ||||||
|  |                 visited.mark(current, true); | ||||||
|  |                 todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |                 ptr_buffer<expr> arg_list; | ||||||
|  |                 for (unsigned i = 0, num_args = to_app(current)->get_num_args(); | ||||||
|  |                      i < num_args; ++i) { | ||||||
|  |                     expr* argument = to_app(current)->get_arg(i); | ||||||
|  | 
 | ||||||
|  |                     if (is_var(argument)) { | ||||||
|  |                         // compute whether there are different
 | ||||||
|  |                         // substitutions for argument
 | ||||||
|  |                         bool containsDifferentSubstitutions = false; | ||||||
|  | 
 | ||||||
|  |                         for (unsigned i=0, sz = m_substitutions.size(); i+1 < sz; ++i) { | ||||||
|  |                             SASSERT(m_substitutions[i].contains(argument)); | ||||||
|  |                             SASSERT(m_substitutions[i+1].contains(argument)); | ||||||
|  | 
 | ||||||
|  |                             // TODO: how to check equality?
 | ||||||
|  |                             if (m_substitutions[i][argument] != | ||||||
|  |                                 m_substitutions[i+1][argument]) | ||||||
|  |                             { | ||||||
|  |                                 containsDifferentSubstitutions = true; | ||||||
|  |                                 break; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         // if yes, use the variable
 | ||||||
|  |                         if (containsDifferentSubstitutions) { | ||||||
|  |                             arg_list.push_back(argument); | ||||||
|  |                         } | ||||||
|  |                         // otherwise use the concrete value instead
 | ||||||
|  |                         // and remove the substitutions
 | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             arg_list.push_back(m_substitutions[0][argument]); | ||||||
|  | 
 | ||||||
|  |                             for (unsigned i=0, sz = m_substitutions.size(); i < sz; ++i) { | ||||||
|  |                                 SASSERT(m_substitutions[i].contains(argument)); | ||||||
|  |                                 m_substitutions[i].remove(argument); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else { | ||||||
|  |                         SASSERT(generalization.contains(argument)); | ||||||
|  |                         arg_list.push_back(generalization[argument]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 SASSERT(to_app(current)->get_num_args() == arg_list.size()); | ||||||
|  |                 expr_ref application(m.mk_app(to_app(current)->get_decl(), | ||||||
|  |                                               to_app(current)->get_num_args(), | ||||||
|  |                                               arg_list.c_ptr()), m); | ||||||
|  |                 m_pinned.push_back(application); | ||||||
|  |                 generalization.insert(current, application); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             todo.pop_back(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_g = generalization[m_g]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ncc_less_than_key | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     ncc_less_than_key(arith_util& util) : m_util(util) {} | ||||||
|  | 
 | ||||||
|  |     bool operator() (const expr*& e1, const expr*& e2) { | ||||||
|  |         rational val1; | ||||||
|  |         rational val2; | ||||||
|  | 
 | ||||||
|  |         if (m_util.is_numeral(e1, val1) && m_util.is_numeral(e2, val2)) | ||||||
|  |         { | ||||||
|  |             return val1 < val2; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             SASSERT(false); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     arith_util m_util; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * if there is a single interval which exactly captures each of the | ||||||
|  |  * substitutions, return the corresponding closure, otherwise do | ||||||
|  |  * nothing | ||||||
|  |  */ | ||||||
|  | bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m, | ||||||
|  |                                            expr_ref& result) { | ||||||
|  |     arith_util util(m); | ||||||
|  | 
 | ||||||
|  |     SASSERT(au.get_num_substitutions() > 0); | ||||||
|  |     if (au.get_substitution(0).size() == 0) { | ||||||
|  |         result = au.get_generalization(); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // check that all substitutions have the same size
 | ||||||
|  |     for (unsigned i=0, sz = au.get_num_substitutions(); i+1 < sz; ++i) { | ||||||
|  |         if (au.get_substitution(i).size() != au.get_substitution(i+1).size()) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // for each substitution entry
 | ||||||
|  |     bool is_first_key = true; | ||||||
|  |     unsigned lower_bound; | ||||||
|  |     unsigned upper_bound; | ||||||
|  |     for (const auto& pair : au.get_substitution(0)) { | ||||||
|  |         // construct vector
 | ||||||
|  |         expr* key = &pair.get_key(); | ||||||
|  |         vector<unsigned> entries; | ||||||
|  | 
 | ||||||
|  |         rational val; | ||||||
|  |         for (unsigned i=0, sz = au.get_num_substitutions(); i < sz; ++i) | ||||||
|  |         { | ||||||
|  |             if (util.is_numeral(au.get_substitution(i)[key], val) && | ||||||
|  |                 val.is_unsigned()) { | ||||||
|  |                 entries.push_back(val.get_unsigned()); | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // check whether vector represents interval
 | ||||||
|  |         unsigned current_lower_bound; | ||||||
|  |         unsigned current_upper_bound; | ||||||
|  | 
 | ||||||
|  |         // if vector represents interval
 | ||||||
|  |         if (get_range(entries, current_lower_bound, current_upper_bound)) { | ||||||
|  |             // if interval is the same as previous interval
 | ||||||
|  |             if (is_first_key) { | ||||||
|  |                 is_first_key = false; | ||||||
|  |                 lower_bound = current_lower_bound; | ||||||
|  |                 upper_bound = current_upper_bound; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 if (current_lower_bound != lower_bound || | ||||||
|  |                     current_upper_bound != upper_bound) { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         // otherwise we don't do a convex closure
 | ||||||
|  |         else { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // we finally know that we can express the substitutions using a
 | ||||||
|  |     // single interval, so build the expression 1. construct const
 | ||||||
|  |     expr_ref const_ref(m.mk_const(symbol("scti!0"), util.mk_int()), m); | ||||||
|  | 
 | ||||||
|  |     // 2. construct body with const
 | ||||||
|  |     expr_ref lit1(util.mk_le(util.mk_int(lower_bound), const_ref), m); | ||||||
|  |     expr_ref lit2(util.mk_le(const_ref, util.mk_int(upper_bound)), m); | ||||||
|  |     expr_ref lit3(m); | ||||||
|  |     substitute_vars_by_const(m, au.get_generalization(), const_ref, lit3); | ||||||
|  | 
 | ||||||
|  |     expr_ref_vector args(m); | ||||||
|  |     args.push_back(lit1); | ||||||
|  |     args.push_back(lit2); | ||||||
|  |     args.push_back(lit3); | ||||||
|  |     expr_ref body_with_consts = mk_and(args); | ||||||
|  | 
 | ||||||
|  |     // 3. replace const by var
 | ||||||
|  |     ptr_vector<expr> vars; | ||||||
|  |     vars.push_back(const_ref); | ||||||
|  | 
 | ||||||
|  |     expr_ref body(m); | ||||||
|  |     expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), body_with_consts, body); | ||||||
|  | 
 | ||||||
|  |     // 4. introduce quantifier
 | ||||||
|  |     ptr_vector<sort> sorts; | ||||||
|  |     sorts.push_back(util.mk_int()); | ||||||
|  |     svector<symbol> names; | ||||||
|  |     names.push_back(symbol("scti!0")); | ||||||
|  | 
 | ||||||
|  |     result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool naive_convex_closure::get_range(vector<unsigned int>& v, | ||||||
|  |                                      unsigned int& lower_bound, unsigned int& upper_bound) | ||||||
|  | { | ||||||
|  |     // sort substitutions
 | ||||||
|  |     std::sort(v.begin(), v.end()); | ||||||
|  | 
 | ||||||
|  |     // check that numbers are consecutive
 | ||||||
|  |     for (unsigned i=0; i+1 < v.size(); ++i) { | ||||||
|  |         if (v[i] + 1 != v[i+1]) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SASSERT(v.size() > 0); | ||||||
|  |     lower_bound = v[0]; | ||||||
|  |     upper_bound = v.back(); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct subs_rewriter_cfg : public default_rewriter_cfg { | ||||||
|  |     ast_manager &m; | ||||||
|  |     expr_ref m_c; | ||||||
|  | 
 | ||||||
|  |     subs_rewriter_cfg (ast_manager &manager, expr* c) : m(manager), m_c(c, m) {} | ||||||
|  | 
 | ||||||
|  |     bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { | ||||||
|  |         result = m_c; | ||||||
|  |         result_pr = 0; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void naive_convex_closure::substitute_vars_by_const(ast_manager& m, expr* t, | ||||||
|  |                                                     expr* c, expr_ref& res) { | ||||||
|  |     subs_rewriter_cfg subs_cfg(m, c); | ||||||
|  |     rewriter_tpl<subs_rewriter_cfg> subs_rw (m, false, subs_cfg); | ||||||
|  |     subs_rw (t, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template class rewriter_tpl<spacer::var_abs_rewriter>; | ||||||
|  | template class rewriter_tpl<spacer::subs_rewriter_cfg>; | ||||||
							
								
								
									
										67
									
								
								src/muz/spacer/spacer_antiunify.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/muz/spacer/spacer_antiunify.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_antiunify.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |   Antiunification utilities | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Bernhard Gleiss | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_ANTIUNIFY_H_ | ||||||
|  | #define _SPACER_ANTIUNIFY_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | class anti_unifier | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     anti_unifier(expr* t, ast_manager& m); | ||||||
|  |     ~anti_unifier() {} | ||||||
|  | 
 | ||||||
|  |     bool add_term(expr* t); | ||||||
|  |     void finalize(); | ||||||
|  | 
 | ||||||
|  |     expr* get_generalization() {return m_g;} | ||||||
|  |     unsigned get_num_substitutions() {return m_substitutions.size();} | ||||||
|  |     obj_map<expr, expr*> get_substitution(unsigned index){ | ||||||
|  |         SASSERT(index < m_substitutions.size()); | ||||||
|  |         return m_substitutions[index]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ast_manager& m; | ||||||
|  |     // tracking all created expressions
 | ||||||
|  |     expr_ref_vector m_pinned; | ||||||
|  | 
 | ||||||
|  |     expr_ref m_g; | ||||||
|  | 
 | ||||||
|  |     vector<obj_map<expr, expr*>> m_substitutions; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class naive_convex_closure | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     static bool compute_closure(anti_unifier& au, ast_manager& m, | ||||||
|  |                                 expr_ref& result); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     static bool get_range(vector<unsigned>& v, unsigned& lower_bound, | ||||||
|  |                           unsigned& upper_bound); | ||||||
|  |     static void substitute_vars_by_const(ast_manager& m, expr* t, expr* c, | ||||||
|  |                                          expr_ref& res); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										3504
									
								
								src/muz/spacer/spacer_context.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3504
									
								
								src/muz/spacer/spacer_context.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										840
									
								
								src/muz/spacer/spacer_context.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										840
									
								
								src/muz/spacer/spacer_context.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,840 @@ | ||||||
|  | /**++
 | ||||||
|  | Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_context.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     SPACER predicate transformers and search context. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  |     Anvesh Komuravelli | ||||||
|  | 
 | ||||||
|  |     Based on muz/pdr/pdr_context.h by Nikolaj Bjorner (nbjorner) | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_CONTEXT_H_ | ||||||
|  | #define _SPACER_CONTEXT_H_ | ||||||
|  | 
 | ||||||
|  | #ifdef _CYGWIN | ||||||
|  | #undef min | ||||||
|  | #undef max | ||||||
|  | #endif | ||||||
|  | #include <queue> | ||||||
|  | #include "spacer_manager.h" | ||||||
|  | #include "spacer_prop_solver.h" | ||||||
|  | #include "fixedpoint_params.hpp" | ||||||
|  | 
 | ||||||
|  | namespace datalog { | ||||||
|  |     class rule_set; | ||||||
|  |     class context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class pred_transformer; | ||||||
|  | class derivation; | ||||||
|  | class pob_queue; | ||||||
|  | class context; | ||||||
|  | 
 | ||||||
|  | typedef obj_map<datalog::rule const, app_ref_vector*> rule2inst; | ||||||
|  | typedef obj_map<func_decl, pred_transformer*> decl2rel; | ||||||
|  | 
 | ||||||
|  | class pob; | ||||||
|  | typedef ref<pob> pob_ref; | ||||||
|  | typedef sref_vector<pob> pob_ref_vector; | ||||||
|  | 
 | ||||||
|  | class reach_fact; | ||||||
|  | typedef ref<reach_fact> reach_fact_ref; | ||||||
|  | typedef sref_vector<reach_fact> reach_fact_ref_vector; | ||||||
|  | 
 | ||||||
|  | class reach_fact { | ||||||
|  |     unsigned m_ref_count; | ||||||
|  | 
 | ||||||
|  |     expr_ref m_fact; | ||||||
|  |     ptr_vector<app> m_aux_vars; | ||||||
|  | 
 | ||||||
|  |     const datalog::rule &m_rule; | ||||||
|  |     reach_fact_ref_vector m_justification; | ||||||
|  | 
 | ||||||
|  |     bool m_init; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     reach_fact (ast_manager &m, const datalog::rule &rule, | ||||||
|  |                 expr* fact, const ptr_vector<app> &aux_vars, | ||||||
|  |                 bool init = false) : | ||||||
|  |         m_ref_count (0), m_fact (fact, m), m_aux_vars (aux_vars), | ||||||
|  |         m_rule(rule), m_init (init) {} | ||||||
|  |     reach_fact (ast_manager &m, const datalog::rule &rule, | ||||||
|  |                 expr* fact, bool init = false) : | ||||||
|  |         m_ref_count (0), m_fact (fact, m), m_rule(rule), m_init (init) {} | ||||||
|  | 
 | ||||||
|  |     bool is_init () {return m_init;} | ||||||
|  |     const datalog::rule& get_rule () {return m_rule;} | ||||||
|  | 
 | ||||||
|  |     void add_justification (reach_fact *f) {m_justification.push_back (f);} | ||||||
|  |     const reach_fact_ref_vector& get_justifications () {return m_justification;} | ||||||
|  | 
 | ||||||
|  |     expr *get () {return m_fact.get ();} | ||||||
|  |     const ptr_vector<app> &aux_vars () {return m_aux_vars;} | ||||||
|  | 
 | ||||||
|  |     void inc_ref () {++m_ref_count;} | ||||||
|  |     void dec_ref () | ||||||
|  |         { | ||||||
|  |             SASSERT (m_ref_count > 0); | ||||||
|  |             --m_ref_count; | ||||||
|  |             if(m_ref_count == 0) { dealloc(this); } | ||||||
|  |         } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class lemma; | ||||||
|  | typedef ref<lemma> lemma_ref; | ||||||
|  | typedef sref_vector<lemma> lemma_ref_vector; | ||||||
|  | 
 | ||||||
|  | typedef pob pob; | ||||||
|  | 
 | ||||||
|  | // a lemma
 | ||||||
|  | class lemma { | ||||||
|  |     unsigned m_ref_count; | ||||||
|  | 
 | ||||||
|  |     ast_manager &m; | ||||||
|  |     expr_ref m_body; | ||||||
|  |     expr_ref_vector m_cube; | ||||||
|  |     app_ref_vector m_bindings; | ||||||
|  |     unsigned m_lvl; | ||||||
|  |     pob_ref m_pob; | ||||||
|  |     bool m_new_pob; | ||||||
|  | 
 | ||||||
|  |     void mk_expr_core(); | ||||||
|  |     void mk_cube_core(); | ||||||
|  | public: | ||||||
|  |     lemma(ast_manager &manager, expr * fml, unsigned lvl); | ||||||
|  |     lemma(pob_ref const &p); | ||||||
|  |     lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl); | ||||||
|  |     lemma(const lemma &other) = delete; | ||||||
|  | 
 | ||||||
|  |     ast_manager &get_ast_manager() {return m;} | ||||||
|  |     expr *get_expr(); | ||||||
|  |     bool is_false(); | ||||||
|  |     expr_ref_vector const &get_cube(); | ||||||
|  |     void update_cube(pob_ref const &p, expr_ref_vector &cube); | ||||||
|  | 
 | ||||||
|  |     bool has_pob() {return m_pob;} | ||||||
|  |     pob_ref &get_pob() {return m_pob;} | ||||||
|  | 
 | ||||||
|  |     unsigned level () const {return m_lvl;} | ||||||
|  |     void set_level (unsigned lvl) {m_lvl = lvl;} | ||||||
|  |     app_ref_vector& get_bindings() {return m_bindings;} | ||||||
|  |     void add_binding(app_ref_vector const &binding) {m_bindings.append(binding);} | ||||||
|  |     void mk_insts(expr_ref_vector& inst, expr* e = nullptr); | ||||||
|  |     bool is_ground () {return !is_quantifier (get_expr());} | ||||||
|  | 
 | ||||||
|  |     void inc_ref () {++m_ref_count;} | ||||||
|  |     void dec_ref () | ||||||
|  |         { | ||||||
|  |             SASSERT (m_ref_count > 0); | ||||||
|  |             --m_ref_count; | ||||||
|  |             if(m_ref_count == 0) { dealloc(this); } | ||||||
|  |         } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct lemma_lt_proc : public std::binary_function<lemma*, lemma *, bool> { | ||||||
|  |     bool operator() (lemma *a, lemma *b) { | ||||||
|  |         return (a->level () < b->level ()) || | ||||||
|  |             (a->level () == b->level () && | ||||||
|  |              ast_lt_proc() (a->get_expr (), b->get_expr ())); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // Predicate transformer state.
 | ||||||
|  | // A predicate transformer corresponds to the
 | ||||||
|  | // set of rules that have the same head predicates.
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | class pred_transformer { | ||||||
|  | 
 | ||||||
|  |     struct stats { | ||||||
|  |         unsigned m_num_propagations; | ||||||
|  |         unsigned m_num_invariants; | ||||||
|  |         stats() { reset(); } | ||||||
|  |         void reset() { memset(this, 0, sizeof(*this)); } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// manager of the lemmas in all the frames
 | ||||||
|  | #include "spacer_legacy_frames.h" | ||||||
|  |     class frames { | ||||||
|  |     private: | ||||||
|  |         pred_transformer &m_pt; | ||||||
|  |         lemma_ref_vector m_lemmas; | ||||||
|  |         unsigned m_size; | ||||||
|  | 
 | ||||||
|  |         bool m_sorted; | ||||||
|  |         lemma_lt_proc m_lt; | ||||||
|  | 
 | ||||||
|  |         void sort (); | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         frames (pred_transformer &pt) : m_pt (pt), m_size(0), m_sorted (true) {} | ||||||
|  |         ~frames() {} | ||||||
|  |         void simplify_formulas (); | ||||||
|  | 
 | ||||||
|  |         pred_transformer& pt () {return m_pt;} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         void get_frame_lemmas (unsigned level, expr_ref_vector &out) { | ||||||
|  |             for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) | ||||||
|  |                 if(m_lemmas[i]->level() == level) { | ||||||
|  |                     out.push_back(m_lemmas[i]->get_expr()); | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  |         void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) { | ||||||
|  |             for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) | ||||||
|  |                 if(m_lemmas [i]->level() >= level) { | ||||||
|  |                     out.push_back(m_lemmas[i]->get_expr()); | ||||||
|  |                 } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         unsigned size () const {return m_size;} | ||||||
|  |         unsigned lemma_size () const {return m_lemmas.size ();} | ||||||
|  |         void add_frame () {m_size++;} | ||||||
|  |         void inherit_frames (frames &other) { | ||||||
|  |             for (unsigned i = 0, sz = other.m_lemmas.size (); i < sz; ++i) { | ||||||
|  |                 lemma_ref lem = alloc(lemma, m_pt.get_ast_manager(), | ||||||
|  |                                       other.m_lemmas[i]->get_expr (), | ||||||
|  |                                       other.m_lemmas[i]->level()); | ||||||
|  |                 lem->add_binding(other.m_lemmas[i]->get_bindings()); | ||||||
|  |                 add_lemma(lem.get()); | ||||||
|  |             } | ||||||
|  |             m_sorted = false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         bool add_lemma (lemma *lem); | ||||||
|  |         void propagate_to_infinity (unsigned level); | ||||||
|  |         bool propagate_to_next_level (unsigned level); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         manager of proof-obligations (pobs) | ||||||
|  |      */ | ||||||
|  |     class pobs { | ||||||
|  |         typedef ptr_buffer<pob, 1> pob_buffer; | ||||||
|  |         typedef obj_map<expr, pob_buffer > expr2pob_buffer; | ||||||
|  | 
 | ||||||
|  |         pred_transformer &m_pt; | ||||||
|  | 
 | ||||||
|  |         expr2pob_buffer m_pobs; | ||||||
|  |         pob_ref_vector m_pinned; | ||||||
|  |     public: | ||||||
|  |         pobs(pred_transformer &pt) : m_pt(pt) {} | ||||||
|  |         pob* mk_pob(pob *parent, unsigned level, unsigned depth, | ||||||
|  |                     expr *post, app_ref_vector const &b); | ||||||
|  | 
 | ||||||
|  |         pob* mk_pob(pob *parent, unsigned level, unsigned depth, | ||||||
|  |                     expr *post) { | ||||||
|  |             app_ref_vector b(m_pt.get_ast_manager()); | ||||||
|  |             return mk_pob (parent, level, depth, post, b); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     typedef obj_map<datalog::rule const, expr*> rule2expr; | ||||||
|  |     typedef obj_map<datalog::rule const, ptr_vector<app> > rule2apps; | ||||||
|  | 
 | ||||||
|  |     manager&                     pm;        // spacer-manager
 | ||||||
|  |     ast_manager&                 m;         // manager
 | ||||||
|  |     context&                     ctx; | ||||||
|  | 
 | ||||||
|  |     func_decl_ref                m_head;    // predicate
 | ||||||
|  |     func_decl_ref_vector         m_sig;     // signature
 | ||||||
|  |     ptr_vector<pred_transformer> m_use;     // places where 'this' is referenced.
 | ||||||
|  |     ptr_vector<datalog::rule>    m_rules;   // rules used to derive transformer
 | ||||||
|  |     prop_solver                  m_solver;  // solver context
 | ||||||
|  |     solver*                      m_reach_ctx; // context for reachability facts
 | ||||||
|  |     pobs                         m_pobs; | ||||||
|  |     frames                       m_frames; | ||||||
|  |     reach_fact_ref_vector        m_reach_facts; // reach facts
 | ||||||
|  |     /// Number of initial reachability facts
 | ||||||
|  |     unsigned                     m_rf_init_sz; | ||||||
|  |     obj_map<expr, datalog::rule const*> m_tag2rule; // map tag predicate to rule.
 | ||||||
|  |     rule2expr                    m_rule2tag;        // map rule to predicate tag.
 | ||||||
|  |     rule2inst                    m_rule2inst;       // map rules to instantiations of indices
 | ||||||
|  |     rule2expr                    m_rule2transition; // map rules to transition
 | ||||||
|  |     rule2apps                    m_rule2vars;       // map rule to auxiliary variables
 | ||||||
|  |     expr_ref                     m_transition;      // transition relation.
 | ||||||
|  |     expr_ref                     m_initial_state;   // initial state.
 | ||||||
|  |     app_ref                      m_extend_lit;      // literal to extend initial state
 | ||||||
|  |     bool                         m_all_init;        // true if the pt has no uninterpreted body in any rule
 | ||||||
|  |     ptr_vector<func_decl>        m_predicates; | ||||||
|  |     stats                        m_stats; | ||||||
|  |     stopwatch                    m_initialize_watch; | ||||||
|  |     stopwatch                    m_must_reachable_watch; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /// Auxiliary variables to represent different disjunctive
 | ||||||
|  |     /// cases of must summaries. Stored over 'n' (a.k.a. new)
 | ||||||
|  |     /// versions of the variables
 | ||||||
|  |     expr_ref_vector              m_reach_case_vars; | ||||||
|  | 
 | ||||||
|  |     void init_sig(); | ||||||
|  |     void ensure_level(unsigned level); | ||||||
|  |     void add_lemma_core (lemma *lemma); | ||||||
|  |     void add_lemma_from_child (pred_transformer &child, lemma *lemma, unsigned lvl); | ||||||
|  | 
 | ||||||
|  |     void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); | ||||||
|  | 
 | ||||||
|  |     // Initialization
 | ||||||
|  |     void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); | ||||||
|  |     void init_rule(decl2rel const& pts, datalog::rule const& rule, vector<bool>& is_init, | ||||||
|  |                    ptr_vector<datalog::rule const>& rules, expr_ref_vector& transition); | ||||||
|  |     void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); | ||||||
|  | 
 | ||||||
|  |     void simplify_formulas(tactic& tac, expr_ref_vector& fmls); | ||||||
|  | 
 | ||||||
|  |     // Debugging
 | ||||||
|  |     bool check_filled(app_ref_vector const& v) const; | ||||||
|  | 
 | ||||||
|  |     void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); | ||||||
|  | 
 | ||||||
|  |     expr* mk_fresh_reach_case_var (); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     pred_transformer(context& ctx, manager& pm, func_decl* head); | ||||||
|  |     ~pred_transformer(); | ||||||
|  | 
 | ||||||
|  |     inline bool use_native_mbp (); | ||||||
|  |     reach_fact *get_reach_fact (expr *v) | ||||||
|  |         { | ||||||
|  |             for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i) | ||||||
|  |                 if(v == m_reach_facts [i]->get()) { return m_reach_facts[i]; } | ||||||
|  |             return NULL; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     void add_rule(datalog::rule* r) { m_rules.push_back(r); } | ||||||
|  |     void add_use(pred_transformer* pt) { if(!m_use.contains(pt)) { m_use.insert(pt); } } | ||||||
|  |     void initialize(decl2rel const& pts); | ||||||
|  | 
 | ||||||
|  |     func_decl* head() const { return m_head; } | ||||||
|  |     ptr_vector<datalog::rule> const& rules() const { return m_rules; } | ||||||
|  |     func_decl* sig(unsigned i) const { return m_sig[i]; } // signature
 | ||||||
|  |     func_decl* const* sig() { return m_sig.c_ptr(); } | ||||||
|  |     unsigned  sig_size() const { return m_sig.size(); } | ||||||
|  |     expr*  transition() const { return m_transition; } | ||||||
|  |     expr*  initial_state() const { return m_initial_state; } | ||||||
|  |     expr*  rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); } | ||||||
|  |     unsigned get_num_levels() { return m_frames.size (); } | ||||||
|  |     expr_ref get_cover_delta(func_decl* p_orig, int level); | ||||||
|  |     void     add_cover(unsigned level, expr* property); | ||||||
|  |     expr_ref get_reachable (); | ||||||
|  | 
 | ||||||
|  |     std::ostream& display(std::ostream& strm) const; | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const; | ||||||
|  |     void reset_statistics(); | ||||||
|  | 
 | ||||||
|  |     bool is_must_reachable (expr* state, model_ref* model = 0); | ||||||
|  |     /// \brief Returns reachability fact active in the given model
 | ||||||
|  |     /// all determines whether initial reachability facts are included as well
 | ||||||
|  |     reach_fact *get_used_reach_fact (model_evaluator_util& mev, bool all = true); | ||||||
|  |     /// \brief Returns reachability fact active in the origin of the given model
 | ||||||
|  |     reach_fact* get_used_origin_reach_fact (model_evaluator_util &mev, unsigned oidx); | ||||||
|  |     expr_ref get_origin_summary (model_evaluator_util &mev, | ||||||
|  |                                  unsigned level, unsigned oidx, bool must, | ||||||
|  |                                  const ptr_vector<app> **aux); | ||||||
|  | 
 | ||||||
|  |     void remove_predecessors(expr_ref_vector& literals); | ||||||
|  |     void find_predecessors(datalog::rule const& r, ptr_vector<func_decl>& predicates) const; | ||||||
|  |     void find_predecessors(vector<std::pair<func_decl*, unsigned> >& predicates) const; | ||||||
|  |     datalog::rule const* find_rule(model &mev, bool& is_concrete, | ||||||
|  |                                    vector<bool>& reach_pred_used, | ||||||
|  |                                    unsigned& num_reuse_reach); | ||||||
|  |     expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } | ||||||
|  |     ptr_vector<app>& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } | ||||||
|  | 
 | ||||||
|  |     bool propagate_to_next_level(unsigned level); | ||||||
|  |     void propagate_to_infinity(unsigned level); | ||||||
|  |     /// \brief  Add a lemma to the current context and all users
 | ||||||
|  |     bool add_lemma(expr * lemma, unsigned lvl); | ||||||
|  |     bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);} | ||||||
|  |     expr* get_reach_case_var (unsigned idx) const; | ||||||
|  |     bool has_reach_facts () const { return !m_reach_facts.empty () ;} | ||||||
|  | 
 | ||||||
|  |     /// initialize reachability facts using initial rules
 | ||||||
|  |     void init_reach_facts (); | ||||||
|  |     void add_reach_fact (reach_fact *fact);  // add reachability fact
 | ||||||
|  |     reach_fact* get_last_reach_fact () const { return m_reach_facts.back (); } | ||||||
|  |     expr* get_last_reach_case_var () const; | ||||||
|  | 
 | ||||||
|  |     pob* mk_pob(pob *parent, unsigned level, unsigned depth, | ||||||
|  |                 expr *post, app_ref_vector const &b){ | ||||||
|  |         return m_pobs.mk_pob(parent, level, depth, post, b); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pob* mk_pob(pob *parent, unsigned level, unsigned depth, | ||||||
|  |                 expr *post) { | ||||||
|  |         return m_pobs.mk_pob(parent, level, depth, post); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lbool is_reachable(pob& n, expr_ref_vector* core, model_ref *model, | ||||||
|  |                        unsigned& uses_level, bool& is_concrete, | ||||||
|  |                        datalog::rule const*& r, | ||||||
|  |                        vector<bool>& reach_pred_used, | ||||||
|  |                        unsigned& num_reuse_reach); | ||||||
|  |     bool is_invariant(unsigned level, expr* lemma, | ||||||
|  |                       unsigned& solver_level, expr_ref_vector* core = 0); | ||||||
|  |     bool check_inductive(unsigned level, expr_ref_vector& state, | ||||||
|  |                          unsigned& assumes_level); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_formulas(unsigned level, bool add_axioms); | ||||||
|  | 
 | ||||||
|  |     void simplify_formulas(); | ||||||
|  | 
 | ||||||
|  |     context& get_context () const {return ctx;} | ||||||
|  |     manager& get_manager() const { return pm; } | ||||||
|  |     ast_manager& get_ast_manager() const { return m; } | ||||||
|  | 
 | ||||||
|  |     void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); | ||||||
|  | 
 | ||||||
|  |     void inherit_properties(pred_transformer& other); | ||||||
|  | 
 | ||||||
|  |     void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector<app>& aux_vars, | ||||||
|  |                           bool is_init); | ||||||
|  | 
 | ||||||
|  |     /// \brief Adds a given expression to the set of initial rules
 | ||||||
|  |     app* extend_initial (expr *e); | ||||||
|  | 
 | ||||||
|  |     /// \brief Returns true if the obligation is already blocked by current lemmas
 | ||||||
|  |     bool is_blocked (pob &n, unsigned &uses_level); | ||||||
|  |     /// \brief Returns true if the obligation is already blocked by current quantified lemmas
 | ||||||
|  |     bool is_qblocked (pob &n); | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * A proof obligation. | ||||||
|  |  */ | ||||||
|  | class pob { | ||||||
|  |     friend class context; | ||||||
|  |     unsigned m_ref_count; | ||||||
|  |     /// parent node
 | ||||||
|  |     pob_ref          m_parent; | ||||||
|  |     /// predicate transformer
 | ||||||
|  |     pred_transformer&       m_pt; | ||||||
|  |     /// post-condition decided by this node
 | ||||||
|  |     expr_ref                m_post; | ||||||
|  |     // if m_post is not ground, then m_binding is an instantiation for
 | ||||||
|  |     // all quantified variables
 | ||||||
|  |     app_ref_vector          m_binding; | ||||||
|  |     /// new post to be swapped in for m_post
 | ||||||
|  |     expr_ref                m_new_post; | ||||||
|  |     /// level at which to decide the post
 | ||||||
|  |     unsigned                m_level; | ||||||
|  | 
 | ||||||
|  |     unsigned                m_depth; | ||||||
|  | 
 | ||||||
|  |     /// whether a concrete answer to the post is found
 | ||||||
|  |     bool                    m_open; | ||||||
|  |     /// whether to use farkas generalizer to construct a lemma blocking this node
 | ||||||
|  |     bool                    m_use_farkas; | ||||||
|  | 
 | ||||||
|  |     unsigned                m_weakness; | ||||||
|  |     /// derivation representing the position of this node in the parent's rule
 | ||||||
|  |     scoped_ptr<derivation>   m_derivation; | ||||||
|  | 
 | ||||||
|  |     ptr_vector<pob>  m_kids; | ||||||
|  | public: | ||||||
|  |     pob (pob* parent, pred_transformer& pt, | ||||||
|  |          unsigned level, unsigned depth=0, bool add_to_parent=true); | ||||||
|  | 
 | ||||||
|  |     ~pob() {if(m_parent) { m_parent->erase_child(*this); }} | ||||||
|  | 
 | ||||||
|  |     unsigned weakness() {return m_weakness;} | ||||||
|  |     void bump_weakness() {m_weakness++;} | ||||||
|  |     void reset_weakness() {m_weakness=0;} | ||||||
|  | 
 | ||||||
|  |     void inc_level () {m_level++; m_depth++;reset_weakness();} | ||||||
|  | 
 | ||||||
|  |     void inherit(pob const &p); | ||||||
|  |     void set_derivation (derivation *d) {m_derivation = d;} | ||||||
|  |     bool has_derivation () const {return (bool)m_derivation;} | ||||||
|  |     derivation &get_derivation() const {return *m_derivation.get ();} | ||||||
|  |     void reset_derivation () {set_derivation (NULL);} | ||||||
|  |     /// detaches derivation from the node without deallocating
 | ||||||
|  |     derivation* detach_derivation () {return m_derivation.detach ();} | ||||||
|  | 
 | ||||||
|  |     pob* parent () const { return m_parent.get (); } | ||||||
|  | 
 | ||||||
|  |     pred_transformer& pt () const { return m_pt; } | ||||||
|  |     ast_manager& get_ast_manager () const { return m_pt.get_ast_manager (); } | ||||||
|  |     manager& get_manager () const { return m_pt.get_manager (); } | ||||||
|  |     context& get_context () const {return m_pt.get_context ();} | ||||||
|  | 
 | ||||||
|  |     unsigned level () const { return m_level; } | ||||||
|  |     unsigned depth () const {return m_depth;} | ||||||
|  | 
 | ||||||
|  |     bool use_farkas_generalizer () const {return m_use_farkas;} | ||||||
|  |     void set_farkas_generalizer (bool v) {m_use_farkas = v;} | ||||||
|  | 
 | ||||||
|  |     expr* post() const { return m_post.get (); } | ||||||
|  |     void set_post(expr *post); | ||||||
|  |     void set_post(expr *post, app_ref_vector const &b); | ||||||
|  | 
 | ||||||
|  |     /// indicate that a new post should be set for the node
 | ||||||
|  |     void new_post(expr *post) {if(post != m_post) {m_new_post = post;}} | ||||||
|  |     /// true if the node needs to be updated outside of the priority queue
 | ||||||
|  |     bool is_dirty () {return m_new_post;} | ||||||
|  |     /// clean a dirty node
 | ||||||
|  |     void clean(); | ||||||
|  | 
 | ||||||
|  |     void reset () {clean (); m_derivation = NULL; m_open = true;} | ||||||
|  | 
 | ||||||
|  |     bool is_closed () const { return !m_open; } | ||||||
|  |     void close(); | ||||||
|  | 
 | ||||||
|  |     void add_child (pob &v) {m_kids.push_back (&v);} | ||||||
|  |     void erase_child (pob &v) {m_kids.erase (&v);} | ||||||
|  | 
 | ||||||
|  |     bool is_ground () { return m_binding.empty (); } | ||||||
|  |     app_ref_vector const &get_binding() const {return m_binding;} | ||||||
|  |     /*
 | ||||||
|  |      * Return skolem variables that appear in post | ||||||
|  |      */ | ||||||
|  |     void get_skolems(app_ref_vector& v); | ||||||
|  | 
 | ||||||
|  |     void inc_ref () {++m_ref_count;} | ||||||
|  |     void dec_ref () | ||||||
|  |         { | ||||||
|  |             --m_ref_count; | ||||||
|  |             if(m_ref_count == 0) { dealloc(this); } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct pob_lt : | ||||||
|  |         public std::binary_function<const pob*, const pob*, bool> | ||||||
|  | {bool operator() (const pob *pn1, const pob *pn2) const;}; | ||||||
|  | 
 | ||||||
|  | struct pob_gt : | ||||||
|  |         public std::binary_function<const pob*, const pob*, bool> { | ||||||
|  |     pob_lt lt; | ||||||
|  |     bool operator() (const pob *n1, const pob *n2) const | ||||||
|  |         {return lt(n2, n1);} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct pob_ref_gt : | ||||||
|  |         public std::binary_function<const pob_ref&, const model_ref &, bool> { | ||||||
|  |     pob_gt gt; | ||||||
|  |     bool operator() (const pob_ref &n1, const pob_ref &n2) const | ||||||
|  |         {return gt (n1.get (), n2.get ());} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  */ | ||||||
|  | class derivation { | ||||||
|  |     /// a single premise of a derivation
 | ||||||
|  |     class premise { | ||||||
|  |         pred_transformer &m_pt; | ||||||
|  |         /// origin order in the rule
 | ||||||
|  |         unsigned m_oidx; | ||||||
|  |         /// summary fact corresponding to the premise
 | ||||||
|  |         expr_ref m_summary; | ||||||
|  |         ///  whether this is a must or may premise
 | ||||||
|  |         bool m_must; | ||||||
|  |         app_ref_vector m_ovars; | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         premise (pred_transformer &pt, unsigned oidx, expr *summary, bool must, | ||||||
|  |                  const ptr_vector<app> *aux_vars = NULL); | ||||||
|  |         premise (const premise &p); | ||||||
|  | 
 | ||||||
|  |         bool is_must () {return m_must;} | ||||||
|  |         expr * get_summary () {return m_summary.get ();} | ||||||
|  |         app_ref_vector &get_ovars () {return m_ovars;} | ||||||
|  |         unsigned get_oidx () {return m_oidx;} | ||||||
|  |         pred_transformer &pt () {return m_pt;} | ||||||
|  | 
 | ||||||
|  |         /// \brief Updated the summary.
 | ||||||
|  |         /// The new summary is over n-variables.
 | ||||||
|  |         void set_summary (expr * summary, bool must, | ||||||
|  |                           const ptr_vector<app> *aux_vars = NULL); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /// parent model node
 | ||||||
|  |     pob&                         m_parent; | ||||||
|  | 
 | ||||||
|  |     /// the rule corresponding to this derivation
 | ||||||
|  |     const datalog::rule &m_rule; | ||||||
|  | 
 | ||||||
|  |     /// the premises
 | ||||||
|  |     vector<premise>                     m_premises; | ||||||
|  |     /// pointer to the active premise
 | ||||||
|  |     unsigned                            m_active; | ||||||
|  |     // transition relation over origin variables
 | ||||||
|  |     expr_ref                            m_trans; | ||||||
|  |     //  implicitly existentially quantified variables in m_trans
 | ||||||
|  |     app_ref_vector                      m_evars; | ||||||
|  |     /// -- create next child using given model as the guide
 | ||||||
|  |     /// -- returns NULL if there is no next child
 | ||||||
|  |     pob* create_next_child (model_evaluator_util &mev); | ||||||
|  | public: | ||||||
|  |     derivation (pob& parent, datalog::rule const& rule, | ||||||
|  |                 expr *trans, app_ref_vector const &evars); | ||||||
|  |     void add_premise (pred_transformer &pt, unsigned oidx, | ||||||
|  |                       expr * summary, bool must, const ptr_vector<app> *aux_vars = NULL); | ||||||
|  | 
 | ||||||
|  |     /// creates the first child. Must be called after all the premises
 | ||||||
|  |     /// are added. The model must be valid for the premises
 | ||||||
|  |     /// Returns NULL if no child exits
 | ||||||
|  |     pob *create_first_child (model_evaluator_util &mev); | ||||||
|  | 
 | ||||||
|  |     /// Create the next child. Must summary of the currently active
 | ||||||
|  |     /// premise must be consistent with the transition relation
 | ||||||
|  |     pob *create_next_child (); | ||||||
|  | 
 | ||||||
|  |     datalog::rule const& get_rule () const { return m_rule; } | ||||||
|  |     pob& get_parent () const { return m_parent; } | ||||||
|  |     ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();} | ||||||
|  |     manager &get_manager () const {return m_parent.get_manager ();} | ||||||
|  |     context &get_context() const {return m_parent.get_context();} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class pob_queue { | ||||||
|  |     pob_ref  m_root; | ||||||
|  |     unsigned m_max_level; | ||||||
|  |     unsigned m_min_depth; | ||||||
|  | 
 | ||||||
|  |     std::priority_queue<pob_ref, std::vector<pob_ref>, | ||||||
|  |                         pob_ref_gt>     m_obligations; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     pob_queue(): m_root(NULL), m_max_level(0), m_min_depth(0) {} | ||||||
|  |     ~pob_queue(); | ||||||
|  | 
 | ||||||
|  |     void reset(); | ||||||
|  |     pob * top (); | ||||||
|  |     void pop () {m_obligations.pop ();} | ||||||
|  |     void push (pob &n) {m_obligations.push (&n);} | ||||||
|  | 
 | ||||||
|  |     void inc_level () | ||||||
|  |         { | ||||||
|  |             SASSERT (!m_obligations.empty () || m_root); | ||||||
|  |             m_max_level++; | ||||||
|  |             m_min_depth++; | ||||||
|  |             if(m_root && m_obligations.empty()) { m_obligations.push(m_root); } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     pob& get_root() const { return *m_root.get (); } | ||||||
|  |     void set_root(pob& n); | ||||||
|  |     bool is_root (pob& n) const {return m_root.get () == &n;} | ||||||
|  | 
 | ||||||
|  |     unsigned max_level () {return m_max_level;} | ||||||
|  |     unsigned min_depth () {return m_min_depth;} | ||||||
|  |     unsigned size () {return m_obligations.size ();} | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Generalizers (strengthens) a lemma | ||||||
|  |  */ | ||||||
|  | class lemma_generalizer { | ||||||
|  | protected: | ||||||
|  |     context& m_ctx; | ||||||
|  | public: | ||||||
|  |     lemma_generalizer(context& ctx): m_ctx(ctx) {} | ||||||
|  |     virtual ~lemma_generalizer() {} | ||||||
|  |     virtual void operator()(lemma_ref &lemma) = 0; | ||||||
|  |     virtual void collect_statistics(statistics& st) const {} | ||||||
|  |     virtual void reset_statistics() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class context { | ||||||
|  | 
 | ||||||
|  |     struct stats { | ||||||
|  |         unsigned m_num_queries; | ||||||
|  |         unsigned m_num_reach_queries; | ||||||
|  |         unsigned m_num_reuse_reach; | ||||||
|  |         unsigned m_max_query_lvl; | ||||||
|  |         unsigned m_max_depth; | ||||||
|  |         unsigned m_cex_depth; | ||||||
|  |         unsigned m_expand_node_undef; | ||||||
|  |         unsigned m_num_lemmas; | ||||||
|  |         unsigned m_num_restarts; | ||||||
|  |         stats() { reset(); } | ||||||
|  |         void reset() { memset(this, 0, sizeof(*this)); } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // stat watches
 | ||||||
|  |     stopwatch m_solve_watch; | ||||||
|  |     stopwatch m_propagate_watch; | ||||||
|  |     stopwatch m_reach_watch; | ||||||
|  |     stopwatch m_is_reach_watch; | ||||||
|  |     stopwatch m_create_children_watch; | ||||||
|  |     stopwatch m_init_rules_watch; | ||||||
|  | 
 | ||||||
|  |     fixedpoint_params const&    m_params; | ||||||
|  |     ast_manager&         m; | ||||||
|  |     datalog::context*    m_context; | ||||||
|  |     manager              m_pm; | ||||||
|  |     decl2rel             m_rels;         // Map from relation predicate to fp-operator.
 | ||||||
|  |     func_decl_ref        m_query_pred; | ||||||
|  |     pred_transformer*    m_query; | ||||||
|  |     mutable pob_queue    m_pob_queue; | ||||||
|  |     lbool                m_last_result; | ||||||
|  |     unsigned             m_inductive_lvl; | ||||||
|  |     unsigned             m_expanded_lvl; | ||||||
|  |     ptr_buffer<lemma_generalizer>  m_lemma_generalizers; | ||||||
|  |     stats                m_stats; | ||||||
|  |     model_converter_ref  m_mc; | ||||||
|  |     proof_converter_ref  m_pc; | ||||||
|  |     bool                 m_use_native_mbp; | ||||||
|  |     bool                 m_ground_cti; | ||||||
|  |     bool                 m_instantiate; | ||||||
|  |     bool                 m_use_qlemmas; | ||||||
|  |     bool                 m_weak_abs; | ||||||
|  |     bool                 m_use_restarts; | ||||||
|  |     unsigned             m_restart_initial_threshold; | ||||||
|  | 
 | ||||||
|  |     // Functions used by search.
 | ||||||
|  |     lbool solve_core (unsigned from_lvl = 0); | ||||||
|  |     bool check_reachability (); | ||||||
|  |     bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, | ||||||
|  |                    unsigned full_prop_lvl); | ||||||
|  |     bool is_reachable(pob &n); | ||||||
|  |     lbool expand_node(pob& n); | ||||||
|  |     reach_fact *mk_reach_fact (pob& n, model_evaluator_util &mev, | ||||||
|  |                                datalog::rule const& r); | ||||||
|  |     bool create_children(pob& n, datalog::rule const& r, | ||||||
|  |                          model_evaluator_util &model, | ||||||
|  |                          const vector<bool>& reach_pred_used); | ||||||
|  |     expr_ref mk_sat_answer(); | ||||||
|  |     expr_ref mk_unsat_answer() const; | ||||||
|  | 
 | ||||||
|  |     // Generate inductive property
 | ||||||
|  |     void get_level_property(unsigned lvl, expr_ref_vector& res, | ||||||
|  |                             vector<relation_info> & rs) const; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // Initialization
 | ||||||
|  |     void init_lemma_generalizers(datalog::rule_set& rules); | ||||||
|  | 
 | ||||||
|  |     bool check_invariant(unsigned lvl); | ||||||
|  |     bool check_invariant(unsigned lvl, func_decl* fn); | ||||||
|  | 
 | ||||||
|  |     void checkpoint(); | ||||||
|  | 
 | ||||||
|  |     void init_rules(datalog::rule_set& rules, decl2rel& transformers); | ||||||
|  | 
 | ||||||
|  |     void simplify_formulas(); | ||||||
|  | 
 | ||||||
|  |     void reset_lemma_generalizers(); | ||||||
|  | 
 | ||||||
|  |     bool validate(); | ||||||
|  | 
 | ||||||
|  |     unsigned get_cex_depth (); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     /**
 | ||||||
|  |        Initial values of predicates are stored in corresponding relations in dctx. | ||||||
|  | 
 | ||||||
|  |        We check whether there is some reachable state of the relation checked_relation. | ||||||
|  |     */ | ||||||
|  |     context( | ||||||
|  |         fixedpoint_params const&  params, | ||||||
|  |         ast_manager&       m); | ||||||
|  | 
 | ||||||
|  |     ~context(); | ||||||
|  | 
 | ||||||
|  |     fixedpoint_params const& get_params() const { return m_params; } | ||||||
|  |     bool use_native_mbp () {return m_use_native_mbp;} | ||||||
|  |     bool use_ground_cti () {return m_ground_cti;} | ||||||
|  |     bool use_instantiate () { return m_instantiate; } | ||||||
|  |     bool use_qlemmas () {return m_use_qlemmas; } | ||||||
|  | 
 | ||||||
|  |     ast_manager&      get_ast_manager() const { return m; } | ||||||
|  |     manager&          get_manager() { return m_pm; } | ||||||
|  |     decl2rel const&   get_pred_transformers() const { return m_rels; } | ||||||
|  |     pred_transformer& get_pred_transformer(func_decl* p) const | ||||||
|  |         { return *m_rels.find(p); } | ||||||
|  |     datalog::context& get_datalog_context() const | ||||||
|  |         { SASSERT(m_context); return *m_context; } | ||||||
|  |     expr_ref          get_answer(); | ||||||
|  |     /**
 | ||||||
|  |      * get bottom-up (from query) sequence of ground predicate instances | ||||||
|  |      * (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query | ||||||
|  |      */ | ||||||
|  |     expr_ref          get_ground_sat_answer (); | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const; | ||||||
|  |     void reset_statistics(); | ||||||
|  | 
 | ||||||
|  |     std::ostream& display(std::ostream& strm) const; | ||||||
|  | 
 | ||||||
|  |     void display_certificate(std::ostream& strm) const {} | ||||||
|  | 
 | ||||||
|  |     lbool solve(unsigned from_lvl = 0); | ||||||
|  | 
 | ||||||
|  |     lbool solve_from_lvl (unsigned from_lvl); | ||||||
|  | 
 | ||||||
|  |     void reset(); | ||||||
|  | 
 | ||||||
|  |     void set_query(func_decl* q) { m_query_pred = q; } | ||||||
|  | 
 | ||||||
|  |     void set_unsat() { m_last_result = l_false; } | ||||||
|  | 
 | ||||||
|  |     void set_model_converter(model_converter_ref& mc) { m_mc = mc; } | ||||||
|  | 
 | ||||||
|  |     void get_rules_along_trace (datalog::rule_ref_vector& rules); | ||||||
|  | 
 | ||||||
|  |     model_converter_ref get_model_converter() { return m_mc; } | ||||||
|  | 
 | ||||||
|  |     void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } | ||||||
|  | 
 | ||||||
|  |     void update_rules(datalog::rule_set& rules); | ||||||
|  | 
 | ||||||
|  |     void set_axioms(expr* axioms) { m_pm.set_background(axioms); } | ||||||
|  | 
 | ||||||
|  |     unsigned get_num_levels(func_decl* p); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); | ||||||
|  | 
 | ||||||
|  |     void add_cover(int level, func_decl* pred, expr* property); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_reachable (func_decl* p); | ||||||
|  | 
 | ||||||
|  |     void add_invariant (func_decl *pred, expr* property); | ||||||
|  | 
 | ||||||
|  |     model_ref get_model(); | ||||||
|  | 
 | ||||||
|  |     proof_ref get_proof() const; | ||||||
|  | 
 | ||||||
|  |     pob& get_root() const { return m_pob_queue.get_root(); } | ||||||
|  | 
 | ||||||
|  |     expr_ref get_constraints (unsigned lvl); | ||||||
|  |     void add_constraints (unsigned lvl, expr_ref c); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										354
									
								
								src/muz/spacer/spacer_dl_interface.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								src/muz/spacer/spacer_dl_interface.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,354 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_dl.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     SMT2 interface for the datalog SPACER | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "dl_context.h" | ||||||
|  | #include "dl_mk_coi_filter.h" | ||||||
|  | #include "dl_mk_interp_tail_simplifier.h" | ||||||
|  | #include "dl_mk_subsumption_checker.h" | ||||||
|  | #include "dl_mk_rule_inliner.h" | ||||||
|  | #include "dl_rule.h" | ||||||
|  | #include "dl_rule_transformer.h" | ||||||
|  | #include "smt2parser.h" | ||||||
|  | #include "spacer_context.h" | ||||||
|  | #include "spacer_dl_interface.h" | ||||||
|  | #include "dl_rule_set.h" | ||||||
|  | #include "dl_mk_slice.h" | ||||||
|  | #include "dl_mk_unfold.h" | ||||||
|  | #include "dl_mk_coalesce.h" | ||||||
|  | #include "model_smt2_pp.h" | ||||||
|  | #include "scoped_proof.h" | ||||||
|  | #include "dl_transforms.h" | ||||||
|  | 
 | ||||||
|  | using namespace spacer; | ||||||
|  | 
 | ||||||
|  | dl_interface::dl_interface(datalog::context& ctx) : | ||||||
|  |     engine_base(ctx.get_manager(), "spacer"), | ||||||
|  |     m_ctx(ctx), | ||||||
|  |     m_spacer_rules(ctx), | ||||||
|  |     m_old_rules(ctx), | ||||||
|  |     m_context(0), | ||||||
|  |     m_refs(ctx.get_manager()) | ||||||
|  | { | ||||||
|  |     m_context = alloc(spacer::context, ctx.get_params(), ctx.get_manager()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | dl_interface::~dl_interface() | ||||||
|  | { | ||||||
|  |     dealloc(m_context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // Check if the new rules are weaker so that we can
 | ||||||
|  | // re-use existing context.
 | ||||||
|  | //
 | ||||||
|  | void dl_interface::check_reset() | ||||||
|  | { | ||||||
|  |     datalog::rule_set const& new_rules = m_ctx.get_rules(); | ||||||
|  |     datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); | ||||||
|  |     bool is_subsumed = !old_rules.empty(); | ||||||
|  |     for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) { | ||||||
|  |         is_subsumed = false; | ||||||
|  |         for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { | ||||||
|  |             if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { | ||||||
|  |                 is_subsumed = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!is_subsumed) { | ||||||
|  |             TRACE("spacer", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); | ||||||
|  |             m_context->reset(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     m_old_rules.replace_rules(new_rules); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | lbool dl_interface::query(expr * query) | ||||||
|  | { | ||||||
|  |     //we restore the initial state in the datalog context
 | ||||||
|  |     m_ctx.ensure_opened(); | ||||||
|  |     m_refs.reset(); | ||||||
|  |     m_pred2slice.reset(); | ||||||
|  |     ast_manager& m =                      m_ctx.get_manager(); | ||||||
|  |     datalog::rule_manager& rm = m_ctx.get_rule_manager(); | ||||||
|  |     datalog::rule_set& rules0 = m_ctx.get_rules(); | ||||||
|  |     datalog::rule_set        old_rules(rules0); | ||||||
|  |     func_decl_ref            query_pred(m); | ||||||
|  |     rm.mk_query(query, m_ctx.get_rules()); | ||||||
|  |     expr_ref bg_assertion = m_ctx.get_background_assertion(); | ||||||
|  | 
 | ||||||
|  |     check_reset(); | ||||||
|  | 
 | ||||||
|  |     TRACE("spacer", | ||||||
|  |     if (!m.is_true(bg_assertion)) { | ||||||
|  |     tout << "axioms:\n"; | ||||||
|  |     tout << mk_pp(bg_assertion, m) << "\n"; | ||||||
|  |     } | ||||||
|  |     tout << "query: " << mk_pp(query, m) << "\n"; | ||||||
|  |          tout << "rules:\n"; | ||||||
|  |          m_ctx.display_rules(tout); | ||||||
|  |          ); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     apply_default_transformation(m_ctx); | ||||||
|  | 
 | ||||||
|  |     if (m_ctx.get_params().xform_slice()) { | ||||||
|  |         datalog::rule_transformer transformer(m_ctx); | ||||||
|  |         datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); | ||||||
|  |         transformer.register_plugin(slice); | ||||||
|  |         m_ctx.transform_rules(transformer); | ||||||
|  | 
 | ||||||
|  |         // track sliced predicates.
 | ||||||
|  |         obj_map<func_decl, func_decl*> const& preds = slice->get_predicates(); | ||||||
|  |         obj_map<func_decl, func_decl*>::iterator it  = preds.begin(); | ||||||
|  |         obj_map<func_decl, func_decl*>::iterator end = preds.end(); | ||||||
|  |         for (; it != end; ++it) { | ||||||
|  |             m_pred2slice.insert(it->m_key, it->m_value); | ||||||
|  |             m_refs.push_back(it->m_key); | ||||||
|  |             m_refs.push_back(it->m_value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m_ctx.get_params().xform_unfold_rules() > 0) { | ||||||
|  |         unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); | ||||||
|  |         datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); | ||||||
|  |         transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); | ||||||
|  |         transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); | ||||||
|  |         if (m_ctx.get_params().xform_coalesce_rules()) { | ||||||
|  |             m_ctx.transform_rules(transf1); | ||||||
|  |         } | ||||||
|  |         while (num_unfolds > 0) { | ||||||
|  |             m_ctx.transform_rules(transf2); | ||||||
|  |             --num_unfolds; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const datalog::rule_set& rules = m_ctx.get_rules(); | ||||||
|  |     if (rules.get_output_predicates().empty()) { | ||||||
|  |         m_context->set_unsat(); | ||||||
|  |         return l_false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     query_pred = rules.get_output_predicate(); | ||||||
|  | 
 | ||||||
|  |     IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); | ||||||
|  |     m_spacer_rules.replace_rules(rules); | ||||||
|  |     m_spacer_rules.close(); | ||||||
|  |     m_ctx.record_transformed_rules(); | ||||||
|  |     m_ctx.reopen(); | ||||||
|  |     m_ctx.replace_rules(old_rules); | ||||||
|  | 
 | ||||||
|  |     scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode.
 | ||||||
|  | 
 | ||||||
|  |     m_context->set_proof_converter(m_ctx.get_proof_converter()); | ||||||
|  |     m_context->set_model_converter(m_ctx.get_model_converter()); | ||||||
|  |     m_context->set_query(query_pred); | ||||||
|  |     m_context->set_axioms(bg_assertion); | ||||||
|  |     m_context->update_rules(m_spacer_rules); | ||||||
|  | 
 | ||||||
|  |     if (m_spacer_rules.get_rules().empty()) { | ||||||
|  |         m_context->set_unsat(); | ||||||
|  |         IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *m_context->get_model(), 0);); | ||||||
|  |         return l_false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return m_context->solve(); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | lbool dl_interface::query_from_lvl(expr * query, unsigned lvl) | ||||||
|  | { | ||||||
|  |     //we restore the initial state in the datalog context
 | ||||||
|  |     m_ctx.ensure_opened(); | ||||||
|  |     m_refs.reset(); | ||||||
|  |     m_pred2slice.reset(); | ||||||
|  |     ast_manager& m =                      m_ctx.get_manager(); | ||||||
|  |     datalog::rule_manager& rm = m_ctx.get_rule_manager(); | ||||||
|  |     datalog::rule_set& rules0 = m_ctx.get_rules(); | ||||||
|  |     datalog::rule_set        old_rules(rules0); | ||||||
|  |     func_decl_ref            query_pred(m); | ||||||
|  |     rm.mk_query(query, m_ctx.get_rules()); | ||||||
|  |     expr_ref bg_assertion = m_ctx.get_background_assertion(); | ||||||
|  | 
 | ||||||
|  |     check_reset(); | ||||||
|  | 
 | ||||||
|  |     TRACE("spacer", | ||||||
|  |     if (!m.is_true(bg_assertion)) { | ||||||
|  |     tout << "axioms:\n"; | ||||||
|  |     tout << mk_pp(bg_assertion, m) << "\n"; | ||||||
|  |     } | ||||||
|  |     tout << "query: " << mk_pp(query, m) << "\n"; | ||||||
|  |          tout << "rules:\n"; | ||||||
|  |          m_ctx.display_rules(tout); | ||||||
|  |          ); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     apply_default_transformation(m_ctx); | ||||||
|  | 
 | ||||||
|  |     if (m_ctx.get_params().xform_slice()) { | ||||||
|  |         datalog::rule_transformer transformer(m_ctx); | ||||||
|  |         datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); | ||||||
|  |         transformer.register_plugin(slice); | ||||||
|  |         m_ctx.transform_rules(transformer); | ||||||
|  | 
 | ||||||
|  |         // track sliced predicates.
 | ||||||
|  |         obj_map<func_decl, func_decl*> const& preds = slice->get_predicates(); | ||||||
|  |         obj_map<func_decl, func_decl*>::iterator it  = preds.begin(); | ||||||
|  |         obj_map<func_decl, func_decl*>::iterator end = preds.end(); | ||||||
|  |         for (; it != end; ++it) { | ||||||
|  |             m_pred2slice.insert(it->m_key, it->m_value); | ||||||
|  |             m_refs.push_back(it->m_key); | ||||||
|  |             m_refs.push_back(it->m_value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m_ctx.get_params().xform_unfold_rules() > 0) { | ||||||
|  |         unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); | ||||||
|  |         datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); | ||||||
|  |         transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); | ||||||
|  |         transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); | ||||||
|  |         if (m_ctx.get_params().xform_coalesce_rules()) { | ||||||
|  |             m_ctx.transform_rules(transf1); | ||||||
|  |         } | ||||||
|  |         while (num_unfolds > 0) { | ||||||
|  |             m_ctx.transform_rules(transf2); | ||||||
|  |             --num_unfolds; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const datalog::rule_set& rules = m_ctx.get_rules(); | ||||||
|  |     if (rules.get_output_predicates().empty()) { | ||||||
|  | 
 | ||||||
|  |         m_context->set_unsat(); | ||||||
|  |         return l_false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     query_pred = rules.get_output_predicate(); | ||||||
|  | 
 | ||||||
|  |     IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); | ||||||
|  |     m_spacer_rules.replace_rules(rules); | ||||||
|  |     m_spacer_rules.close(); | ||||||
|  |     m_ctx.record_transformed_rules(); | ||||||
|  |     m_ctx.reopen(); | ||||||
|  |     m_ctx.replace_rules(old_rules); | ||||||
|  | 
 | ||||||
|  |     scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode.
 | ||||||
|  | 
 | ||||||
|  |     m_context->set_proof_converter(m_ctx.get_proof_converter()); | ||||||
|  |     m_context->set_model_converter(m_ctx.get_model_converter()); | ||||||
|  |     m_context->set_query(query_pred); | ||||||
|  |     m_context->set_axioms(bg_assertion); | ||||||
|  |     m_context->update_rules(m_spacer_rules); | ||||||
|  | 
 | ||||||
|  |     if (m_spacer_rules.get_rules().empty()) { | ||||||
|  |         m_context->set_unsat(); | ||||||
|  |         IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(), 0);); | ||||||
|  |         return l_false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return m_context->solve(lvl); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) | ||||||
|  | { | ||||||
|  |     func_decl* pred = pred_orig; | ||||||
|  |     m_pred2slice.find(pred_orig, pred); | ||||||
|  |     SASSERT(pred); | ||||||
|  |     return m_context->get_cover_delta(level, pred_orig, pred); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::add_cover(int level, func_decl* pred, expr* property) | ||||||
|  | { | ||||||
|  |     if (m_ctx.get_params().xform_slice()) { | ||||||
|  |         throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers"); | ||||||
|  |     } | ||||||
|  |     m_context->add_cover(level, pred, property); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::add_invariant(func_decl* pred, expr* property) | ||||||
|  | { | ||||||
|  |     if (m_ctx.get_params().xform_slice()) { | ||||||
|  |         throw default_exception("Invariants are incompatible with slicing. Disable slicing before using invariants"); | ||||||
|  |     } | ||||||
|  |     m_context->add_invariant(pred, property); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref dl_interface::get_reachable(func_decl* pred) | ||||||
|  | { | ||||||
|  |     if (m_ctx.get_params().xform_slice()) { | ||||||
|  |         throw default_exception("Invariants are incompatible with slicing. " | ||||||
|  |                                 "Disable slicing before using invariants"); | ||||||
|  |     } | ||||||
|  |     return m_context->get_reachable(pred); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsigned dl_interface::get_num_levels(func_decl* pred) | ||||||
|  | { | ||||||
|  |     m_pred2slice.find(pred, pred); | ||||||
|  |     SASSERT(pred); | ||||||
|  |     return m_context->get_num_levels(pred); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::collect_statistics(statistics& st) const | ||||||
|  | { | ||||||
|  |     m_context->collect_statistics(st); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::reset_statistics() | ||||||
|  | { | ||||||
|  |     m_context->reset_statistics(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::display_certificate(std::ostream& out) const | ||||||
|  | { | ||||||
|  |     m_context->display_certificate(out); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref dl_interface::get_answer() | ||||||
|  | { | ||||||
|  |     return m_context->get_answer(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref dl_interface::get_ground_sat_answer() | ||||||
|  | { | ||||||
|  |     return m_context->get_ground_sat_answer(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::get_rules_along_trace(datalog::rule_ref_vector& rules) | ||||||
|  | { | ||||||
|  |     m_context->get_rules_along_trace(rules); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dl_interface::updt_params() | ||||||
|  | { | ||||||
|  |     dealloc(m_context); | ||||||
|  |     m_context = alloc(spacer::context, m_ctx.get_params(), m_ctx.get_manager()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | model_ref dl_interface::get_model() | ||||||
|  | { | ||||||
|  |     return m_context->get_model(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | proof_ref dl_interface::get_proof() | ||||||
|  | { | ||||||
|  |     return m_context->get_proof(); | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								src/muz/spacer/spacer_dl_interface.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/muz/spacer/spacer_dl_interface.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_dl_interface.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     SMT2 interface for the datalog SPACER | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_DL_INTERFACE_H_ | ||||||
|  | #define _SPACER_DL_INTERFACE_H_ | ||||||
|  | 
 | ||||||
|  | #include "lbool.h" | ||||||
|  | #include "dl_rule.h" | ||||||
|  | #include "dl_rule_set.h" | ||||||
|  | #include "dl_engine_base.h" | ||||||
|  | #include "statistics.h" | ||||||
|  | 
 | ||||||
|  | namespace datalog { | ||||||
|  | class context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class context; | ||||||
|  | 
 | ||||||
|  | class dl_interface : public datalog::engine_base { | ||||||
|  |     datalog::context& m_ctx; | ||||||
|  |     datalog::rule_set m_spacer_rules; | ||||||
|  |     datalog::rule_set m_old_rules; | ||||||
|  |     context*          m_context; | ||||||
|  |     obj_map<func_decl, func_decl*> m_pred2slice; | ||||||
|  |     ast_ref_vector    m_refs; | ||||||
|  | 
 | ||||||
|  |     void check_reset(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     dl_interface(datalog::context& ctx); | ||||||
|  |     ~dl_interface(); | ||||||
|  | 
 | ||||||
|  |     lbool query(expr* query); | ||||||
|  | 
 | ||||||
|  |     lbool query_from_lvl(expr* query, unsigned lvl); | ||||||
|  | 
 | ||||||
|  |     void display_certificate(std::ostream& out) const; | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const; | ||||||
|  | 
 | ||||||
|  |     void reset_statistics(); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_answer(); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_ground_sat_answer(); | ||||||
|  | 
 | ||||||
|  |     void get_rules_along_trace(datalog::rule_ref_vector& rules); | ||||||
|  | 
 | ||||||
|  |     unsigned get_num_levels(func_decl* pred); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_cover_delta(int level, func_decl* pred); | ||||||
|  | 
 | ||||||
|  |     void add_cover(int level, func_decl* pred, expr* property); | ||||||
|  | 
 | ||||||
|  |     void add_invariant(func_decl* pred, expr* property); | ||||||
|  | 
 | ||||||
|  |     expr_ref get_reachable(func_decl *pred); | ||||||
|  | 
 | ||||||
|  |     void updt_params(); | ||||||
|  | 
 | ||||||
|  |     model_ref get_model(); | ||||||
|  | 
 | ||||||
|  |     proof_ref get_proof(); | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										440
									
								
								src/muz/spacer/spacer_farkas_learner.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								src/muz/spacer/spacer_farkas_learner.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,440 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_farkas_learner.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Proviced abstract interface and some inplementations of algorithms | ||||||
|  |     for strenghtning lemmas | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-11-1. | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | // TODO: what to write here
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | //TODO: reorder, delete unnecessary includes
 | ||||||
|  | #include "ast_smt2_pp.h" | ||||||
|  | #include "array_decl_plugin.h" | ||||||
|  | #include "bool_rewriter.h" | ||||||
|  | #include "dl_decl_plugin.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "dl_util.h" | ||||||
|  | #include "rewriter.h" | ||||||
|  | #include "rewriter_def.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "spacer_farkas_learner.h" | ||||||
|  | #include "th_rewriter.h" | ||||||
|  | #include "ast_ll_pp.h" | ||||||
|  | #include "proof_utils.h" | ||||||
|  | #include "reg_decl_plugins.h" | ||||||
|  | #include "smt_farkas_util.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class collect_pure_proc { | ||||||
|  |     func_decl_set& m_symbs; | ||||||
|  | public: | ||||||
|  |     collect_pure_proc(func_decl_set& s): m_symbs(s) {} | ||||||
|  | 
 | ||||||
|  |     void operator()(app* a) | ||||||
|  |     { | ||||||
|  |         if (a->get_family_id() == null_family_id) { | ||||||
|  |             m_symbs.insert(a->get_decl()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator()(var*) {} | ||||||
|  |     void operator()(quantifier*) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res) | ||||||
|  | { | ||||||
|  |     ast_manager& m = res.get_manager(); | ||||||
|  |     smt::farkas_util res_c(m); | ||||||
|  |     res_c.set_split_literals(m_split_literals); | ||||||
|  |     for (unsigned i = 0; i < n; ++i) { | ||||||
|  |         res_c.add(coeffs[i], lits[i]); | ||||||
|  |     } | ||||||
|  |     res = res_c.get(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // every uninterpreted symbol is in symbs
 | ||||||
|  | class is_pure_expr_proc { | ||||||
|  |     func_decl_set const& m_symbs; | ||||||
|  |     array_util           m_au; | ||||||
|  | public: | ||||||
|  |     struct non_pure {}; | ||||||
|  | 
 | ||||||
|  |     is_pure_expr_proc(func_decl_set const& s, ast_manager& m): | ||||||
|  |         m_symbs(s), | ||||||
|  |         m_au(m) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     void operator()(app* a) | ||||||
|  |     { | ||||||
|  |         if (a->get_family_id() == null_family_id) { | ||||||
|  |             if (!m_symbs.contains(a->get_decl())) { | ||||||
|  |                 throw non_pure(); | ||||||
|  |             } | ||||||
|  |         } else if (a->get_family_id() == m_au.get_family_id() && | ||||||
|  |                    a->is_app_of(a->get_family_id(), OP_ARRAY_EXT)) { | ||||||
|  |             throw non_pure(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator()(var*) {} | ||||||
|  |     void operator()(quantifier*) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool farkas_learner::is_pure_expr(func_decl_set const& symbs, expr* e, ast_manager& m) const | ||||||
|  | { | ||||||
|  |     is_pure_expr_proc proc(symbs, m); | ||||||
|  |     try { | ||||||
|  |         for_each_expr(proc, e); | ||||||
|  |     } catch (is_pure_expr_proc::non_pure) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |    Revised version of Farkas strengthener. | ||||||
|  |    1. Mark B-pure nodes as derivations that depend only on B. | ||||||
|  |    2. Collect B-influenced nodes | ||||||
|  |    3. (optional) Permute B-pure units over resolution steps to narrow dependencies on B. | ||||||
|  |    4. Weaken B-pure units for resolution with Farkas Clauses. | ||||||
|  |    5. Add B-pure units elsewhere. | ||||||
|  | 
 | ||||||
|  |    Rules: | ||||||
|  |    - hypothesis h |- h | ||||||
|  | 
 | ||||||
|  |                 H |- false | ||||||
|  |    - lemma      ---------- | ||||||
|  |                  |- not H | ||||||
|  | 
 | ||||||
|  |                 Th |- L \/ C   H |- not L | ||||||
|  |    - th-lemma   ------------------------- | ||||||
|  |                        H  |- C | ||||||
|  | 
 | ||||||
|  |      Note: C is false for theory axioms, C is unit literal for propagation. | ||||||
|  | 
 | ||||||
|  |    - rewrite        |- t = s | ||||||
|  | 
 | ||||||
|  |                     H |- t = s | ||||||
|  |    - monotonicity   ---------------- | ||||||
|  |                    H |- f(t) = f(s) | ||||||
|  | 
 | ||||||
|  |                     H |- t = s H' |- s = u | ||||||
|  |    - trans          ---------------------- | ||||||
|  |                         H, H' |- t = u | ||||||
|  | 
 | ||||||
|  |                     H |- C \/ L  H' |- not L | ||||||
|  |    - unit_resolve   ------------------------ | ||||||
|  |                             H, H' |- C | ||||||
|  | 
 | ||||||
|  |                     H |- a ~ b   H' |- a | ||||||
|  |    - mp             -------------------- | ||||||
|  |                          H, H' |- b | ||||||
|  | 
 | ||||||
|  |    - def-axiom       |- C | ||||||
|  | 
 | ||||||
|  |    - asserted        |- f | ||||||
|  | 
 | ||||||
|  |    Mark nodes by: | ||||||
|  |       - Hypotheses | ||||||
|  |       - Dependency on bs | ||||||
|  |       - Dependency on A | ||||||
|  | 
 | ||||||
|  |    A node is unit derivable from bs if: | ||||||
|  |       - It has no hypotheses. | ||||||
|  |       - It depends on bs. | ||||||
|  |       - It does not depend on A. | ||||||
|  | 
 | ||||||
|  |    NB: currently unit derivable is not symmetric: A clause can be | ||||||
|  |    unit derivable, but a unit literal with hypotheses is not. | ||||||
|  |    This is clearly wrong, because hypotheses are just additional literals | ||||||
|  |    in a clausal version. | ||||||
|  | 
 | ||||||
|  |    NB: the routine is not interpolating, though an interpolating variant would | ||||||
|  |    be preferrable because then we can also use it for model propagation. | ||||||
|  | 
 | ||||||
|  |    We collect the unit derivable nodes from bs. | ||||||
|  |    These are the weakenings of bs, besides the | ||||||
|  |    units under Farkas. | ||||||
|  | 
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #define INSERT(_x_) if (!lemma_set.contains(_x_)) { lemma_set.insert(_x_); lemmas.push_back(_x_); } | ||||||
|  | 
 | ||||||
|  | void farkas_learner::get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas) | ||||||
|  | { | ||||||
|  |     ast_manager& m = lemmas.get_manager(); | ||||||
|  |     bool_rewriter brwr(m); | ||||||
|  |     func_decl_set Bsymbs; | ||||||
|  |     collect_pure_proc collect_proc(Bsymbs); | ||||||
|  |     expr_set::iterator it = bs.begin(), end = bs.end(); | ||||||
|  |     for (; it != end; ++it) { | ||||||
|  |         for_each_expr(collect_proc, *it); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     proof_ref pr(root, m); | ||||||
|  |     proof_utils::reduce_hypotheses(pr); | ||||||
|  |     proof_utils::permute_unit_resolution(pr); | ||||||
|  |     IF_VERBOSE(3, verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";); | ||||||
|  | 
 | ||||||
|  |     ptr_vector<expr_set> hyprefs; | ||||||
|  |     obj_map<expr, expr_set*> hypmap; | ||||||
|  |     obj_hashtable<expr> lemma_set; | ||||||
|  |     ast_mark b_depend, a_depend, visited, b_closed; | ||||||
|  |     expr_set* empty_set = alloc(expr_set); | ||||||
|  |     hyprefs.push_back(empty_set); | ||||||
|  |     ptr_vector<proof> todo; | ||||||
|  |     TRACE("spacer_verbose", tout << mk_pp(pr, m) << "\n";); | ||||||
|  |     todo.push_back(pr); | ||||||
|  |     while (!todo.empty()) { | ||||||
|  |         proof* p = todo.back(); | ||||||
|  |         SASSERT(m.is_proof(p)); | ||||||
|  |         if (visited.is_marked(p)) { | ||||||
|  |             todo.pop_back(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         bool all_visit = true; | ||||||
|  |         for (unsigned i = 0; i < m.get_num_parents(p); ++i) { | ||||||
|  |             expr* arg = p->get_arg(i); | ||||||
|  |             SASSERT(m.is_proof(arg)); | ||||||
|  |             if (!visited.is_marked(arg)) { | ||||||
|  |                 all_visit = false; | ||||||
|  |                 todo.push_back(to_app(arg)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!all_visit) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         visited.mark(p, true); | ||||||
|  |         todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |         // retrieve hypotheses and dependencies on A, bs.
 | ||||||
|  |         bool b_dep = false, a_dep = false; | ||||||
|  |         expr_set* hyps = empty_set; | ||||||
|  |         for (unsigned i = 0; i < m.get_num_parents(p); ++i) { | ||||||
|  |             expr* arg = p->get_arg(i); | ||||||
|  |             a_dep = a_dep || a_depend.is_marked(arg); | ||||||
|  |             b_dep = b_dep || b_depend.is_marked(arg); | ||||||
|  |             expr_set* hyps2 = hypmap.find(arg); | ||||||
|  |             if (hyps != hyps2 && !hyps2->empty()) { | ||||||
|  |                 if (hyps->empty()) { | ||||||
|  |                     hyps = hyps2; | ||||||
|  |                 } else { | ||||||
|  |                     expr_set* hyps3 = alloc(expr_set); | ||||||
|  |                     datalog::set_union(*hyps3, *hyps); | ||||||
|  |                     datalog::set_union(*hyps3, *hyps2); | ||||||
|  |                     hyps = hyps3; | ||||||
|  |                     hyprefs.push_back(hyps); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         hypmap.insert(p, hyps); | ||||||
|  |         a_depend.mark(p, a_dep); | ||||||
|  |         b_depend.mark(p, b_dep); | ||||||
|  | 
 | ||||||
|  | #define IS_B_PURE(_p) (b_depend.is_marked(_p) && !a_depend.is_marked(_p) && hypmap.find(_p)->empty()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         // Add lemmas that depend on bs, have no hypotheses, don't depend on A.
 | ||||||
|  |         if ((!hyps->empty() || a_depend.is_marked(p)) && | ||||||
|  |                 b_depend.is_marked(p) && !is_farkas_lemma(m, p)) { | ||||||
|  |             for (unsigned i = 0; i < m.get_num_parents(p); ++i) { | ||||||
|  |                 app* arg = to_app(p->get_arg(i)); | ||||||
|  |                 if (IS_B_PURE(arg)) { | ||||||
|  |                     expr* fact = m.get_fact(arg); | ||||||
|  |                     if (is_pure_expr(Bsymbs, fact, m)) { | ||||||
|  |                         TRACE("farkas_learner2", | ||||||
|  |                               tout << "Add: " << mk_pp(m.get_fact(arg), m) << "\n"; | ||||||
|  |                               tout << mk_pp(arg, m) << "\n"; | ||||||
|  |                              ); | ||||||
|  |                         INSERT(fact); | ||||||
|  |                     } else { | ||||||
|  |                         get_asserted(p, bs, b_closed, lemma_set, lemmas); | ||||||
|  |                         b_closed.mark(p, true); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         switch (p->get_decl_kind()) { | ||||||
|  |         case PR_ASSERTED: | ||||||
|  |             if (bs.contains(m.get_fact(p))) { | ||||||
|  |                 b_depend.mark(p, true); | ||||||
|  |             } else { | ||||||
|  |                 a_depend.mark(p, true); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case PR_HYPOTHESIS: { | ||||||
|  |             SASSERT(hyps == empty_set); | ||||||
|  |             hyps = alloc(expr_set); | ||||||
|  |             hyps->insert(m.get_fact(p)); | ||||||
|  |             hyprefs.push_back(hyps); | ||||||
|  |             hypmap.insert(p, hyps); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case PR_DEF_AXIOM: { | ||||||
|  |             if (!is_pure_expr(Bsymbs, m.get_fact(p), m)) { | ||||||
|  |                 a_depend.mark(p, true); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case PR_LEMMA: { | ||||||
|  |             expr_set* hyps2 = alloc(expr_set); | ||||||
|  |             hyprefs.push_back(hyps2); | ||||||
|  |             datalog::set_union(*hyps2, *hyps); | ||||||
|  |             hyps = hyps2; | ||||||
|  |             expr* fml = m.get_fact(p); | ||||||
|  |             hyps->remove(fml); | ||||||
|  |             if (m.is_or(fml)) { | ||||||
|  |                 for (unsigned i = 0; i < to_app(fml)->get_num_args(); ++i) { | ||||||
|  |                     expr* f = to_app(fml)->get_arg(i); | ||||||
|  |                     expr_ref hyp(m); | ||||||
|  |                     brwr.mk_not(f, hyp); | ||||||
|  |                     hyps->remove(hyp); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             hypmap.insert(p, hyps); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case PR_TH_LEMMA: { | ||||||
|  |             if (!is_farkas_lemma(m, p)) { break; } | ||||||
|  | 
 | ||||||
|  |             SASSERT(m.has_fact(p)); | ||||||
|  |             unsigned prem_cnt = m.get_num_parents(p); | ||||||
|  |             func_decl * d = p->get_decl(); | ||||||
|  |             SASSERT(d->get_num_parameters() >= prem_cnt + 2); | ||||||
|  |             SASSERT(d->get_parameter(0).get_symbol() == "arith"); | ||||||
|  |             SASSERT(d->get_parameter(1).get_symbol() == "farkas"); | ||||||
|  |             parameter const* params = d->get_parameters() + 2; | ||||||
|  | 
 | ||||||
|  |             app_ref_vector lits(m); | ||||||
|  |             expr_ref tmp(m); | ||||||
|  |             unsigned num_b_pures = 0; | ||||||
|  |             rational coef; | ||||||
|  |             vector<rational> coeffs; | ||||||
|  | 
 | ||||||
|  |             TRACE("farkas_learner2", | ||||||
|  |             for (unsigned i = 0; i < prem_cnt; ++i) { | ||||||
|  |             VERIFY(params[i].is_rational(coef)); | ||||||
|  |                 proof* prem = to_app(p->get_arg(i)); | ||||||
|  |                 bool b_pure = IS_B_PURE(prem); | ||||||
|  |                 tout << (b_pure ? "B" : "A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; | ||||||
|  |             } | ||||||
|  |             tout << mk_pp(m.get_fact(p), m) << "\n"; | ||||||
|  |                  ); | ||||||
|  | 
 | ||||||
|  |             // NB. Taking 'abs' of coefficients is a workaround.
 | ||||||
|  |             // The Farkas coefficient extraction in arith_core must be wrong.
 | ||||||
|  |             // The coefficients would be always positive relative to the theory lemma.
 | ||||||
|  | 
 | ||||||
|  |             for (unsigned i = 0; i < prem_cnt; ++i) { | ||||||
|  |                 expr * prem_e = p->get_arg(i); | ||||||
|  |                 SASSERT(is_app(prem_e)); | ||||||
|  |                 proof * prem = to_app(prem_e); | ||||||
|  | 
 | ||||||
|  |                 if (IS_B_PURE(prem)) { | ||||||
|  |                     ++num_b_pures; | ||||||
|  |                 } else { | ||||||
|  |                     VERIFY(params[i].is_rational(coef)); | ||||||
|  |                     lits.push_back(to_app(m.get_fact(prem))); | ||||||
|  |                     coeffs.push_back(abs(coef)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             params += prem_cnt; | ||||||
|  |             if (prem_cnt + 2 < d->get_num_parameters()) { | ||||||
|  |                 unsigned num_args = 1; | ||||||
|  |                 expr* fact = m.get_fact(p); | ||||||
|  |                 expr* const* args = &fact; | ||||||
|  |                 if (m.is_or(fact)) { | ||||||
|  |                     app* _or = to_app(fact); | ||||||
|  |                     num_args = _or->get_num_args(); | ||||||
|  |                     args = _or->get_args(); | ||||||
|  |                 } | ||||||
|  |                 SASSERT(prem_cnt + 2 + num_args == d->get_num_parameters()); | ||||||
|  |                 for (unsigned i = 0; i < num_args; ++i) { | ||||||
|  |                     expr* prem_e = args[i]; | ||||||
|  |                     brwr.mk_not(prem_e, tmp); | ||||||
|  |                     VERIFY(params[i].is_rational(coef)); | ||||||
|  |                     SASSERT(is_app(tmp)); | ||||||
|  |                     lits.push_back(to_app(tmp)); | ||||||
|  |                     coeffs.push_back(abs(coef)); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |             SASSERT(coeffs.size() == lits.size()); | ||||||
|  |             if (num_b_pures > 0) { | ||||||
|  |                 expr_ref res(m); | ||||||
|  |                 combine_constraints(coeffs.size(), lits.c_ptr(), coeffs.c_ptr(), res); | ||||||
|  |                 TRACE("farkas_learner2", tout << "Add: " << mk_pp(res, m) << "\n";); | ||||||
|  |                 INSERT(res); | ||||||
|  |                 b_closed.mark(p, true); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::for_each(hyprefs.begin(), hyprefs.end(), delete_proc<expr_set>()); | ||||||
|  |     simplify_bounds(lemmas); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable<expr>& lemma_set, expr_ref_vector& lemmas) | ||||||
|  | { | ||||||
|  |     ast_manager& m = lemmas.get_manager(); | ||||||
|  |     ast_mark visited; | ||||||
|  |     proof* p0 = p; | ||||||
|  |     ptr_vector<proof> todo; | ||||||
|  |     todo.push_back(p); | ||||||
|  | 
 | ||||||
|  |     while (!todo.empty()) { | ||||||
|  |         p = todo.back(); | ||||||
|  |         todo.pop_back(); | ||||||
|  |         if (visited.is_marked(p) || b_closed.is_marked(p)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         visited.mark(p, true); | ||||||
|  |         for (unsigned i = 0; i < m.get_num_parents(p); ++i) { | ||||||
|  |             expr* arg = p->get_arg(i); | ||||||
|  |             SASSERT(m.is_proof(arg)); | ||||||
|  |             todo.push_back(to_app(arg)); | ||||||
|  |         } | ||||||
|  |         if (p->get_decl_kind() == PR_ASSERTED && | ||||||
|  |                 bs.contains(m.get_fact(p))) { | ||||||
|  |             expr* fact = m.get_fact(p); | ||||||
|  |             TRACE("farkas_learner2", | ||||||
|  |                   tout << mk_ll_pp(p0, m) << "\n"; | ||||||
|  |                   tout << "Add: " << mk_pp(p, m) << "\n";); | ||||||
|  |             INSERT(fact); | ||||||
|  |             b_closed.mark(p, true); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool farkas_learner::is_farkas_lemma(ast_manager& m, expr* e) | ||||||
|  | { | ||||||
|  |     app * a; | ||||||
|  |     func_decl* d; | ||||||
|  |     symbol sym; | ||||||
|  |     return | ||||||
|  |         is_app(e) && | ||||||
|  |         (a = to_app(e), d = a->get_decl(), true) && | ||||||
|  |         PR_TH_LEMMA == a->get_decl_kind() && | ||||||
|  |         d->get_num_parameters() >= 2 && | ||||||
|  |         d->get_parameter(0).is_symbol(sym) && sym == "arith" && | ||||||
|  |         d->get_parameter(1).is_symbol(sym) && sym == "farkas" && | ||||||
|  |         d->get_num_parameters() >= m.get_num_parents(to_app(e)) + 2; | ||||||
|  | } | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										66
									
								
								src/muz/spacer/spacer_farkas_learner.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/muz/spacer/spacer_farkas_learner.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_farkas_learner.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     SMT2 interface for the datalog SPACER | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-11-1. | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_FARKAS_LEARNER_H_ | ||||||
|  | #define _SPACER_FARKAS_LEARNER_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class farkas_learner { | ||||||
|  |     typedef obj_hashtable<expr> expr_set; | ||||||
|  | 
 | ||||||
|  |     bool m_split_literals; | ||||||
|  | 
 | ||||||
|  |     void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res); | ||||||
|  | 
 | ||||||
|  |     bool is_farkas_lemma(ast_manager& m, expr* e); | ||||||
|  | 
 | ||||||
|  |     void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable<expr>& lemma_set, expr_ref_vector& lemmas); | ||||||
|  | 
 | ||||||
|  |     bool is_pure_expr(func_decl_set const& symbs, expr* e, ast_manager& m) const; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     farkas_learner(): m_split_literals(false) {} | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         Traverse a proof and retrieve lemmas using the vocabulary from bs. | ||||||
|  |     */ | ||||||
|  |     void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas); | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const {} | ||||||
|  |     void reset_statistics() {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** \brief see smt::farkas_util::set_split_literals */ | ||||||
|  |     void set_split_literals(bool v) {m_split_literals = v;} | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										294
									
								
								src/muz/spacer/spacer_generalizers.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								src/muz/spacer/spacer_generalizers.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,294 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_generalizers.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Lemma generalizers. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2011-11-20. | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include "spacer_context.h" | ||||||
|  | #include "spacer_generalizers.h" | ||||||
|  | #include "expr_abstract.h" | ||||||
|  | #include "var_subst.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "obj_equiv_class.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | void lemma_sanity_checker::operator()(lemma_ref &lemma) { | ||||||
|  |     unsigned uses_level; | ||||||
|  |     expr_ref_vector cube(lemma->get_ast_manager()); | ||||||
|  |     cube.append(lemma->get_cube()); | ||||||
|  |     ENSURE(lemma->get_pob()->pt().check_inductive(lemma->level(), | ||||||
|  |                                                   cube, uses_level)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // ------------------------
 | ||||||
|  | // lemma_bool_inductive_generalizer
 | ||||||
|  | /// Inductive generalization by dropping and expanding literals
 | ||||||
|  | void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { | ||||||
|  |     if (lemma->get_cube().empty()) return; | ||||||
|  | 
 | ||||||
|  |     m_st.count++; | ||||||
|  |     scoped_watch _w_(m_st.watch); | ||||||
|  | 
 | ||||||
|  |     unsigned uses_level; | ||||||
|  |     pred_transformer &pt = lemma->get_pob()->pt(); | ||||||
|  |     ast_manager &m = pt.get_ast_manager(); | ||||||
|  | 
 | ||||||
|  |     expr_ref_vector cube(m); | ||||||
|  |     cube.append(lemma->get_cube()); | ||||||
|  | 
 | ||||||
|  |     bool dirty = false; | ||||||
|  |     expr_ref true_expr(m.mk_true(), m); | ||||||
|  |     ptr_vector<expr> processed; | ||||||
|  |     expr_ref_vector extra_lits(m); | ||||||
|  | 
 | ||||||
|  |     unsigned i = 0, num_failures = 0; | ||||||
|  |     while (i < cube.size() && | ||||||
|  |            (!m_failure_limit || num_failures < m_failure_limit)) { | ||||||
|  |         expr_ref lit(m); | ||||||
|  |         lit = cube.get(i); | ||||||
|  |         cube[i] = true_expr; | ||||||
|  |         if (cube.size() > 1 && | ||||||
|  |             pt.check_inductive(lemma->level(), cube, uses_level)) { | ||||||
|  |             num_failures = 0; | ||||||
|  |             dirty = true; | ||||||
|  |             for (i = 0; i < cube.size() && | ||||||
|  |                      processed.contains(cube.get(i)); ++i); | ||||||
|  |         } else { | ||||||
|  |             // check if the literal can be expanded and any single
 | ||||||
|  |             // literal in the expansion can replace it
 | ||||||
|  |             extra_lits.reset(); | ||||||
|  |             extra_lits.push_back(lit); | ||||||
|  |             expand_literals(m, extra_lits); | ||||||
|  |             SASSERT(extra_lits.size() > 0); | ||||||
|  |             bool found = false; | ||||||
|  |             if (extra_lits.get(0) != lit) { | ||||||
|  |                 SASSERT(extra_lits.size() > 1); | ||||||
|  |                 for (unsigned j = 0, sz = extra_lits.size(); !found && j < sz; ++j) { | ||||||
|  |                     cube[i] = extra_lits.get(j); | ||||||
|  |                     if (pt.check_inductive(lemma->level(), cube, uses_level)) { | ||||||
|  |                         num_failures = 0; | ||||||
|  |                         dirty = true; | ||||||
|  |                         found = true; | ||||||
|  |                         processed.push_back(extra_lits.get(j)); | ||||||
|  |                         for (i = 0; i < cube.size() && | ||||||
|  |                                  processed.contains(cube.get(i)); ++i); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (!found) { | ||||||
|  |                 cube[i] = lit; | ||||||
|  |                 processed.push_back(lit); | ||||||
|  |                 ++num_failures; | ||||||
|  |                 ++m_st.num_failures; | ||||||
|  |                 ++i; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (dirty) { | ||||||
|  |         TRACE("spacer", | ||||||
|  |                tout << "Generalized from:\n" << mk_and(lemma->get_cube()) | ||||||
|  |                << "\ninto\n" << mk_and(cube) << "\n";); | ||||||
|  | 
 | ||||||
|  |         lemma->update_cube(lemma->get_pob(), cube); | ||||||
|  |         SASSERT(uses_level >= lemma->level()); | ||||||
|  |         lemma->set_level(uses_level); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void lemma_bool_inductive_generalizer::collect_statistics(statistics &st) const | ||||||
|  | { | ||||||
|  |     st.update("time.spacer.solve.reach.gen.bool_ind", m_st.watch.get_seconds()); | ||||||
|  |     st.update("bool inductive gen", m_st.count); | ||||||
|  |     st.update("bool inductive gen failures", m_st.num_failures); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_generalizer::operator()(lemma_ref &lemma) | ||||||
|  | { | ||||||
|  |     m_st.count++; | ||||||
|  |     scoped_watch _w_(m_st.watch); | ||||||
|  |     ast_manager &m = lemma->get_ast_manager(); | ||||||
|  | 
 | ||||||
|  |     pred_transformer &pt = lemma->get_pob()->pt(); | ||||||
|  | 
 | ||||||
|  |     unsigned old_sz = lemma->get_cube().size(); | ||||||
|  |     unsigned old_level = lemma->level(); | ||||||
|  | 
 | ||||||
|  |     unsigned uses_level; | ||||||
|  |     expr_ref_vector core(m); | ||||||
|  |     bool r; | ||||||
|  |     r = pt.is_invariant(lemma->level(), lemma->get_expr(), uses_level, &core); | ||||||
|  |     SASSERT(r); | ||||||
|  | 
 | ||||||
|  |     CTRACE("spacer", old_sz > core.size(), | ||||||
|  |            tout << "unsat core reduced lemma from: " | ||||||
|  |            << old_sz << " to " << core.size() << "\n";); | ||||||
|  |     CTRACE("spacer", old_level < uses_level, | ||||||
|  |            tout << "unsat core moved lemma up from: " | ||||||
|  |            << old_level << " to " << uses_level << "\n";); | ||||||
|  |     if (old_sz > core.size()) { | ||||||
|  |         lemma->update_cube(lemma->get_pob(), core); | ||||||
|  |         lemma->set_level(uses_level); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_generalizer::collect_statistics(statistics &st) const | ||||||
|  | { | ||||||
|  |     st.update("time.spacer.solve.reach.gen.unsat_core", m_st.watch.get_seconds()); | ||||||
|  |     st.update("gen.unsat_core.cnt", m_st.count); | ||||||
|  |     st.update("gen.unsat_core.fail", m_st.num_failures); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | class collect_array_proc { | ||||||
|  |     array_util m_au; | ||||||
|  |     func_decl_set &m_symbs; | ||||||
|  |     sort *m_sort; | ||||||
|  | public: | ||||||
|  |     collect_array_proc(ast_manager &m, func_decl_set& s) : | ||||||
|  |         m_au(m), m_symbs(s), m_sort(NULL) {} | ||||||
|  | 
 | ||||||
|  |     void operator()(app* a) | ||||||
|  |     { | ||||||
|  |         if (a->get_family_id() == null_family_id && m_au.is_array(a)) { | ||||||
|  |             if (m_sort && m_sort != get_sort(a)) { return; } | ||||||
|  |             if (!m_sort) { m_sort = get_sort(a); } | ||||||
|  |             m_symbs.insert(a->get_decl()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator()(var*) {} | ||||||
|  |     void operator()(quantifier*) {} | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) | ||||||
|  | { | ||||||
|  |     TRACE("core_array_eq", tout << "Looking for equalities\n";); | ||||||
|  | 
 | ||||||
|  |     // -- find array constants
 | ||||||
|  |     ast_manager &m = lemma->get_ast_manager(); | ||||||
|  |     manager &pm = m_ctx.get_manager(); | ||||||
|  | 
 | ||||||
|  |     expr_ref_vector core(m); | ||||||
|  |     expr_ref v(m); | ||||||
|  |     func_decl_set symb; | ||||||
|  |     collect_array_proc cap(m, symb); | ||||||
|  | 
 | ||||||
|  |     core.append (lemma->get_cube()); | ||||||
|  |     v = mk_and(core); | ||||||
|  |     for_each_expr(cap, v); | ||||||
|  | 
 | ||||||
|  |     TRACE("core_array_eq", | ||||||
|  |           tout << "found " << symb.size() << " array variables in: \n" | ||||||
|  |           << mk_pp(v, m) << "\n";); | ||||||
|  | 
 | ||||||
|  |     // too few constants
 | ||||||
|  |     if (symb.size() <= 1) { return; } | ||||||
|  |     // too many constants, skip this
 | ||||||
|  |     if (symb.size() >= 8) { return; } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // -- for every pair of variables, try an equality
 | ||||||
|  |     typedef func_decl_set::iterator iterator; | ||||||
|  |     ptr_vector<func_decl> vsymbs; | ||||||
|  |     for (iterator it = symb.begin(), end = symb.end(); | ||||||
|  |             it != end; ++it) | ||||||
|  |     { vsymbs.push_back(*it); } | ||||||
|  | 
 | ||||||
|  |     expr_ref_vector eqs(m); | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) | ||||||
|  |         for (unsigned j = i + 1; j < sz; ++j) | ||||||
|  |         { eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), | ||||||
|  |                                 m.mk_const(vsymbs.get(j)))); } | ||||||
|  | 
 | ||||||
|  |     smt::kernel solver(m, m_ctx.get_manager().fparams2()); | ||||||
|  |     expr_ref_vector lits(m); | ||||||
|  |     for (unsigned i = 0, core_sz = core.size(); i < core_sz; ++i) { | ||||||
|  |         SASSERT(lits.size() == i); | ||||||
|  |         solver.push(); | ||||||
|  |         solver.assert_expr(core.get(i)); | ||||||
|  |         for (unsigned j = 0, eqs_sz = eqs.size(); j < eqs_sz; ++j) { | ||||||
|  |             solver.push(); | ||||||
|  |             solver.assert_expr(eqs.get(j)); | ||||||
|  |             lbool res = solver.check(); | ||||||
|  |             solver.pop(1); | ||||||
|  | 
 | ||||||
|  |             if (res == l_false) { | ||||||
|  |                 TRACE("core_array_eq", | ||||||
|  |                       tout << "strengthened " << mk_pp(core.get(i), m) | ||||||
|  |                       << " with " << mk_pp(m.mk_not(eqs.get(j)), m) << "\n";); | ||||||
|  |                 lits.push_back(m.mk_not(eqs.get(j))); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         solver.pop(1); | ||||||
|  |         if (lits.size() == i) { lits.push_back(core.get(i)); } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        HACK: if the first 3 arguments of pt are boolean, assume | ||||||
|  |        they correspond to SeaHorn encoding and condition the equality on them. | ||||||
|  |     */ | ||||||
|  |     // pred_transformer &pt = n.pt ();
 | ||||||
|  |     // if (pt.sig_size () >= 3 &&
 | ||||||
|  |     //     m.is_bool (pt.sig (0)->get_range ()) &&
 | ||||||
|  |     //     m.is_bool (pt.sig (1)->get_range ()) &&
 | ||||||
|  |     //     m.is_bool (pt.sig (2)->get_range ()))
 | ||||||
|  |     // {
 | ||||||
|  |     //   lits.push_back (m.mk_const (pm.o2n(pt.sig (0), 0)));
 | ||||||
|  |     //   lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (1), 0))));
 | ||||||
|  |     //   lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (2), 0))));
 | ||||||
|  |     // }
 | ||||||
|  | 
 | ||||||
|  |     TRACE("core_array_eq", tout << "new possible core " | ||||||
|  |           << mk_pp(pm.mk_and(lits), m) << "\n";); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     pred_transformer &pt = lemma->get_pob()->pt(); | ||||||
|  |     // -- check if it is consistent with the transition relation
 | ||||||
|  |     unsigned uses_level1; | ||||||
|  |     if (pt.check_inductive(lemma->level(), lits, uses_level1)) { | ||||||
|  |         TRACE("core_array_eq", tout << "Inductive!\n";); | ||||||
|  |         lemma->update_cube(lemma->get_pob(),lits); | ||||||
|  |         lemma->set_level(uses_level1); | ||||||
|  |         return; | ||||||
|  |     } else | ||||||
|  |     { TRACE("core_array_eq", tout << "Not-Inductive!\n";);} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void lemma_eq_generalizer::operator() (lemma_ref &lemma) | ||||||
|  | { | ||||||
|  |     TRACE("core_eq", tout << "Transforming equivalence classes\n";); | ||||||
|  | 
 | ||||||
|  |     ast_manager &m = m_ctx.get_ast_manager(); | ||||||
|  |     expr_ref_vector core(m); | ||||||
|  |     core.append (lemma->get_cube()); | ||||||
|  | 
 | ||||||
|  |     bool dirty; | ||||||
|  |     expr_equiv_class eq_classes(m); | ||||||
|  |     factor_eqs(core, eq_classes); | ||||||
|  |     // create all possible equalities to allow for simple inductive generalization
 | ||||||
|  |     dirty = equiv_to_expr_full(eq_classes, core); | ||||||
|  |     if (dirty) { | ||||||
|  |         lemma->update_cube(lemma->get_pob(), core); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | }; | ||||||
							
								
								
									
										99
									
								
								src/muz/spacer/spacer_generalizers.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/muz/spacer/spacer_generalizers.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_generalizers.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Generalizer plugins. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2011-11-22. | ||||||
|  |     Arie Gurfinkel | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_GENERALIZERS_H_ | ||||||
|  | #define _SPACER_GENERALIZERS_H_ | ||||||
|  | 
 | ||||||
|  | #include "spacer_context.h" | ||||||
|  | #include "arith_decl_plugin.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | // can be used to check whether produced core is really implied by
 | ||||||
|  | // frame and therefore valid TODO: or negation?
 | ||||||
|  | class lemma_sanity_checker : public lemma_generalizer { | ||||||
|  | public: | ||||||
|  |     lemma_sanity_checker(context& ctx) : lemma_generalizer(ctx) {} | ||||||
|  |     virtual ~lemma_sanity_checker() {} | ||||||
|  |     virtual void operator()(lemma_ref &lemma); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Boolean inductive generalization by dropping literals | ||||||
|  |  */ | ||||||
|  | class lemma_bool_inductive_generalizer : public lemma_generalizer { | ||||||
|  | 
 | ||||||
|  |     struct stats { | ||||||
|  |         unsigned count; | ||||||
|  |         unsigned num_failures; | ||||||
|  |         stopwatch watch; | ||||||
|  |         stats() {reset();} | ||||||
|  |         void reset() {count = 0; num_failures = 0; watch.reset();} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     unsigned m_failure_limit; | ||||||
|  |     stats m_st; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : | ||||||
|  |         lemma_generalizer(ctx), m_failure_limit(failure_limit) {} | ||||||
|  |     virtual ~lemma_bool_inductive_generalizer() {} | ||||||
|  |     virtual void operator()(lemma_ref &lemma); | ||||||
|  | 
 | ||||||
|  |     virtual void collect_statistics(statistics& st) const; | ||||||
|  |     virtual void reset_statistics() {m_st.reset();} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class unsat_core_generalizer : public lemma_generalizer { | ||||||
|  |     struct stats { | ||||||
|  |         unsigned count; | ||||||
|  |         unsigned num_failures; | ||||||
|  |         stopwatch watch; | ||||||
|  |         stats() { reset(); } | ||||||
|  |         void reset() {count = 0; num_failures = 0; watch.reset();} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     stats m_st; | ||||||
|  | public: | ||||||
|  |     unsat_core_generalizer(context &ctx) : lemma_generalizer(ctx) {} | ||||||
|  |     virtual ~unsat_core_generalizer() {} | ||||||
|  |     virtual void operator()(lemma_ref &lemma); | ||||||
|  | 
 | ||||||
|  |     virtual void collect_statistics(statistics &st) const; | ||||||
|  |     virtual void reset_statistics() {m_st.reset();} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class lemma_array_eq_generalizer : public lemma_generalizer { | ||||||
|  | public: | ||||||
|  |     lemma_array_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} | ||||||
|  |     virtual ~lemma_array_eq_generalizer() {} | ||||||
|  |     virtual void operator()(lemma_ref &lemma); | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class lemma_eq_generalizer : public lemma_generalizer { | ||||||
|  | public: | ||||||
|  |     lemma_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} | ||||||
|  |     virtual ~lemma_eq_generalizer() {} | ||||||
|  |     virtual void operator()(lemma_ref &lemma); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | #endif | ||||||
							
								
								
									
										355
									
								
								src/muz/spacer/spacer_itp_solver.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								src/muz/spacer/spacer_itp_solver.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,355 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_itp_solver.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    A solver that produces interpolated unsat cores | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include"spacer_itp_solver.h" | ||||||
|  | #include"ast.h" | ||||||
|  | #include"spacer_util.h" | ||||||
|  | #include"spacer_farkas_learner.h" | ||||||
|  | #include"expr_replacer.h" | ||||||
|  | #include "spacer_unsat_core_learner.h" | ||||||
|  | #include "spacer_unsat_core_plugin.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | void itp_solver::push () | ||||||
|  | { | ||||||
|  |     m_defs.push_back (def_manager (*this)); | ||||||
|  |     m_solver.push (); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::pop (unsigned n) | ||||||
|  | { | ||||||
|  |     m_solver.pop (n); | ||||||
|  |     unsigned lvl = m_defs.size (); | ||||||
|  |     SASSERT (n <= lvl); | ||||||
|  |     unsigned new_lvl = lvl-n; | ||||||
|  |     while (m_defs.size() > new_lvl) { | ||||||
|  |         m_num_proxies -= m_defs.back ().m_defs.size (); | ||||||
|  |         m_defs.pop_back (); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | app* itp_solver::fresh_proxy () | ||||||
|  | { | ||||||
|  |     if (m_num_proxies == m_proxies.size()) { | ||||||
|  |         std::stringstream name; | ||||||
|  |         name << "spacer_proxy!" << m_proxies.size (); | ||||||
|  |         app_ref res(m); | ||||||
|  |         res = m.mk_const (symbol (name.str ().c_str ()), | ||||||
|  |                           m.mk_bool_sort ()); | ||||||
|  |         m_proxies.push_back (res); | ||||||
|  | 
 | ||||||
|  |         // -- add the new proxy to proxy eliminator
 | ||||||
|  |         proof_ref pr(m); | ||||||
|  |         pr = m.mk_asserted (m.mk_true ()); | ||||||
|  |         m_elim_proxies_sub.insert (res, m.mk_true (), pr); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     return m_proxies.get (m_num_proxies++); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | app* itp_solver::mk_proxy (expr *v) | ||||||
|  | { | ||||||
|  |     { | ||||||
|  |         expr *e = v; | ||||||
|  |         m.is_not (v, e); | ||||||
|  |         if (is_uninterp_const(e)) { return to_app(v); } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs; | ||||||
|  |     return def.mk_proxy (v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool itp_solver::mk_proxies (expr_ref_vector &v, unsigned from) | ||||||
|  | { | ||||||
|  |     bool dirty = false; | ||||||
|  |     for (unsigned i = from, sz = v.size(); i < sz; ++i) { | ||||||
|  |         app *p = mk_proxy (v.get (i)); | ||||||
|  |         dirty |= (v.get (i) != p); | ||||||
|  |         v[i] = p; | ||||||
|  |     } | ||||||
|  |     return dirty; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::push_bg (expr *e) | ||||||
|  | { | ||||||
|  |     if (m_assumptions.size () > m_first_assumption) | ||||||
|  |     { m_assumptions.shrink(m_first_assumption); } | ||||||
|  |     m_assumptions.push_back (e); | ||||||
|  |     m_first_assumption = m_assumptions.size (); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::pop_bg (unsigned n) | ||||||
|  | { | ||||||
|  |     if (n == 0) { return; } | ||||||
|  | 
 | ||||||
|  |     if (m_assumptions.size () > m_first_assumption) | ||||||
|  |     { m_assumptions.shrink(m_first_assumption); } | ||||||
|  |     m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; | ||||||
|  |     m_assumptions.shrink (m_first_assumption); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsigned itp_solver::get_num_bg () {return m_first_assumption;} | ||||||
|  | 
 | ||||||
|  | lbool itp_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) | ||||||
|  | { | ||||||
|  |     // -- remove any old assumptions
 | ||||||
|  |     if (m_assumptions.size () > m_first_assumption) | ||||||
|  |     { m_assumptions.shrink(m_first_assumption); } | ||||||
|  | 
 | ||||||
|  |     // -- replace theory literals in background assumptions with proxies
 | ||||||
|  |     mk_proxies (m_assumptions); | ||||||
|  |     // -- in case mk_proxies added new literals, they are all background
 | ||||||
|  |     m_first_assumption = m_assumptions.size (); | ||||||
|  | 
 | ||||||
|  |     m_assumptions.append (num_assumptions, assumptions); | ||||||
|  |     m_is_proxied = mk_proxies (m_assumptions, m_first_assumption); | ||||||
|  | 
 | ||||||
|  |     lbool res; | ||||||
|  |     res = m_solver.check_sat (m_assumptions.size (), m_assumptions.c_ptr ()); | ||||||
|  |     set_status (res); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | app* itp_solver::def_manager::mk_proxy (expr *v) | ||||||
|  | { | ||||||
|  |     app* r; | ||||||
|  |     if (m_expr2proxy.find(v, r)) { return r; } | ||||||
|  | 
 | ||||||
|  |     ast_manager &m = m_parent.m; | ||||||
|  |     app_ref proxy(m); | ||||||
|  |     app_ref def(m); | ||||||
|  |     proxy = m_parent.fresh_proxy (); | ||||||
|  |     def = m.mk_or (m.mk_not (proxy), v); | ||||||
|  |     m_defs.push_back (def); | ||||||
|  |     m_expr2proxy.insert (v, proxy); | ||||||
|  |     m_proxy2def.insert (proxy, def); | ||||||
|  | 
 | ||||||
|  |     m_parent.assert_expr (def.get ()); | ||||||
|  |     return proxy; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool itp_solver::def_manager::is_proxy (app *k, app_ref &def) | ||||||
|  | { | ||||||
|  |     app *r = NULL; | ||||||
|  |     bool found = m_proxy2def.find (k, r); | ||||||
|  |     def = r; | ||||||
|  |     return found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::def_manager::reset () | ||||||
|  | { | ||||||
|  |     m_expr2proxy.reset (); | ||||||
|  |     m_proxy2def.reset (); | ||||||
|  |     m_defs.reset (); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool itp_solver::def_manager::is_proxy_def (expr *v) | ||||||
|  | { | ||||||
|  |     // XXX This might not be the most robust way to check
 | ||||||
|  |     return m_defs.contains (v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool itp_solver::is_proxy(expr *e, app_ref &def) | ||||||
|  | { | ||||||
|  |     if (!is_uninterp_const(e)) { return false; } | ||||||
|  | 
 | ||||||
|  |     app *a = to_app (e); | ||||||
|  | 
 | ||||||
|  |     for (int i = m_defs.size (); i > 0; --i) | ||||||
|  |         if (m_defs[i-1].is_proxy (a, def)) | ||||||
|  |         { return true; } | ||||||
|  | 
 | ||||||
|  |     if (m_base_defs.is_proxy (a, def)) | ||||||
|  |     { return true; } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::collect_statistics (statistics &st) const | ||||||
|  | { | ||||||
|  |     m_solver.collect_statistics (st); | ||||||
|  |     st.update ("time.itp_solver.itp_core", m_itp_watch.get_seconds ()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::reset_statistics () | ||||||
|  | { | ||||||
|  |     m_itp_watch.reset (); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::get_unsat_core (ptr_vector<expr> &core) | ||||||
|  | { | ||||||
|  |     m_solver.get_unsat_core (core); | ||||||
|  |     undo_proxies_in_core (core); | ||||||
|  | } | ||||||
|  | void itp_solver::undo_proxies_in_core (ptr_vector<expr> &r) | ||||||
|  | { | ||||||
|  |     app_ref e(m); | ||||||
|  |     expr_fast_mark1 bg; | ||||||
|  |     for (unsigned i = 0; i < m_first_assumption; ++i) | ||||||
|  |     { bg.mark(m_assumptions.get(i)); } | ||||||
|  | 
 | ||||||
|  |     // expand proxies
 | ||||||
|  |     unsigned j = 0; | ||||||
|  |     for (unsigned i = 0, sz = r.size(); i < sz; ++i) { | ||||||
|  |         // skip background assumptions
 | ||||||
|  |         if (bg.is_marked(r[i])) { continue; } | ||||||
|  | 
 | ||||||
|  |         // -- undo proxies, but only if they were introduced in check_sat
 | ||||||
|  |         if (m_is_proxied && is_proxy(r[i], e)) { | ||||||
|  |             SASSERT (m.is_or (e)); | ||||||
|  |             r[j] = e->get_arg (1); | ||||||
|  |         } else if (i != j) { r[j] = r[i]; } | ||||||
|  |         j++; | ||||||
|  |     } | ||||||
|  |     r.shrink (j); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::undo_proxies (expr_ref_vector &r) | ||||||
|  | { | ||||||
|  |     app_ref e(m); | ||||||
|  |     // expand proxies
 | ||||||
|  |     for (unsigned i = 0, sz = r.size (); i < sz; ++i) | ||||||
|  |         if (is_proxy(r.get(i), e)) { | ||||||
|  |             SASSERT (m.is_or (e)); | ||||||
|  |             r[i] = e->get_arg (1); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::get_unsat_core (expr_ref_vector &_core) | ||||||
|  | { | ||||||
|  |     ptr_vector<expr> core; | ||||||
|  |     get_unsat_core (core); | ||||||
|  |     _core.append (core.size (), core.c_ptr ()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::elim_proxies (expr_ref_vector &v) | ||||||
|  | { | ||||||
|  |     expr_ref f = mk_and (v); | ||||||
|  |     scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer (m); | ||||||
|  |     rep->set_substitution (&m_elim_proxies_sub); | ||||||
|  |     (*rep) (f); | ||||||
|  |     v.reset (); | ||||||
|  |     flatten_and (f, v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::get_itp_core (expr_ref_vector &core) | ||||||
|  | { | ||||||
|  |     scoped_watch _t_ (m_itp_watch); | ||||||
|  | 
 | ||||||
|  |     typedef obj_hashtable<expr> expr_set; | ||||||
|  |     expr_set B; | ||||||
|  |     for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) { | ||||||
|  |         expr *a = m_assumptions.get (i); | ||||||
|  |         app_ref def(m); | ||||||
|  |         if (is_proxy(a, def)) { B.insert(def.get()); } | ||||||
|  |         B.insert (a); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     proof_ref pr(m); | ||||||
|  |     pr = get_proof (); | ||||||
|  | 
 | ||||||
|  |     if (!m_new_unsat_core) { | ||||||
|  |         // old code
 | ||||||
|  |         farkas_learner learner_old; | ||||||
|  |         learner_old.set_split_literals(m_split_literals); | ||||||
|  | 
 | ||||||
|  |         learner_old.get_lemmas (pr, B, core); | ||||||
|  |         elim_proxies (core); | ||||||
|  |         simplify_bounds (core); // XXX potentially redundant
 | ||||||
|  |     } else { | ||||||
|  |         // new code
 | ||||||
|  |         unsat_core_learner learner(m); | ||||||
|  | 
 | ||||||
|  |         if (m_farkas_optimized) { | ||||||
|  |             if (true) // TODO: proper options
 | ||||||
|  |             { | ||||||
|  |                 unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); | ||||||
|  |                 learner.register_plugin(plugin_farkas_lemma_optimized); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); | ||||||
|  |                 learner.register_plugin(plugin_farkas_lemma_bounded); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, m_farkas_a_const); | ||||||
|  |             learner.register_plugin(plugin_farkas_lemma); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (m_minimize_unsat_core) { | ||||||
|  |             unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); | ||||||
|  |             learner.register_plugin(plugin_min_cut); | ||||||
|  |         } else { | ||||||
|  |             unsat_core_plugin_lemma* plugin_lemma = alloc(unsat_core_plugin_lemma, learner); | ||||||
|  |             learner.register_plugin(plugin_lemma); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         learner.compute_unsat_core(pr, B, core); | ||||||
|  | 
 | ||||||
|  |         elim_proxies (core); | ||||||
|  |         simplify_bounds (core); // XXX potentially redundant
 | ||||||
|  | 
 | ||||||
|  | //            // debug
 | ||||||
|  | //            expr_ref_vector core2(m);
 | ||||||
|  | //            unsat_core_learner learner2(m);
 | ||||||
|  | //
 | ||||||
|  | //            unsat_core_plugin_farkas_lemma* plugin_farkas_lemma2 = alloc(unsat_core_plugin_farkas_lemma, learner2, m_split_literals);
 | ||||||
|  | //            learner2.register_plugin(plugin_farkas_lemma2);
 | ||||||
|  | //            unsat_core_plugin_lemma* plugin_lemma2 = alloc(unsat_core_plugin_lemma, learner2);
 | ||||||
|  | //            learner2.register_plugin(plugin_lemma2);
 | ||||||
|  | //            learner2.compute_unsat_core(pr, B, core2);
 | ||||||
|  | //
 | ||||||
|  | //            elim_proxies (core2);
 | ||||||
|  | //            simplify_bounds (core2);
 | ||||||
|  | //
 | ||||||
|  | //            IF_VERBOSE(2,
 | ||||||
|  | //                       verbose_stream () << "Itp Core:\n"
 | ||||||
|  | //                       << mk_pp (mk_and (core), m) << "\n";);
 | ||||||
|  | //            IF_VERBOSE(2,
 | ||||||
|  | //                       verbose_stream () << "Itp Core2:\n"
 | ||||||
|  | //                       << mk_pp (mk_and (core2), m) << "\n";);
 | ||||||
|  |         //SASSERT(mk_and (core) == mk_and (core2));
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     IF_VERBOSE(2, | ||||||
|  |                verbose_stream () << "Itp Core:\n" | ||||||
|  |                << mk_pp (mk_and (core), m) << "\n";); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void itp_solver::refresh () | ||||||
|  | { | ||||||
|  |     // only refresh in non-pushed state
 | ||||||
|  |     SASSERT (m_defs.size () == 0); | ||||||
|  |     expr_ref_vector assertions (m); | ||||||
|  |     for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) { | ||||||
|  |         expr* a = m_solver.get_assertion (i); | ||||||
|  |         if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |     m_base_defs.reset (); | ||||||
|  |     NOT_IMPLEMENTED_YET (); | ||||||
|  |     // solver interface does not have a reset method. need to introduce it somewhere.
 | ||||||
|  |     // m_solver.reset ();
 | ||||||
|  |     for (unsigned i = 0, e = assertions.size (); i < e; ++i) | ||||||
|  |     { m_solver.assert_expr(assertions.get(i)); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										177
									
								
								src/muz/spacer/spacer_itp_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/muz/spacer/spacer_itp_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,177 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_itp_solver.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    A solver that produces interpolated unsat cores | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef SPACER_ITP_SOLVER_H_ | ||||||
|  | #define SPACER_ITP_SOLVER_H_ | ||||||
|  | 
 | ||||||
|  | #include "solver.h" | ||||||
|  | #include "expr_substitution.h" | ||||||
|  | #include"stopwatch.h" | ||||||
|  | namespace spacer { | ||||||
|  | class itp_solver : public solver { | ||||||
|  | private: | ||||||
|  |     struct def_manager { | ||||||
|  |         itp_solver &m_parent; | ||||||
|  |         obj_map<expr, app*> m_expr2proxy; | ||||||
|  |         obj_map<app, app*> m_proxy2def; | ||||||
|  | 
 | ||||||
|  |         expr_ref_vector m_defs; | ||||||
|  | 
 | ||||||
|  |         def_manager(itp_solver &parent) : | ||||||
|  |             m_parent(parent), m_defs(m_parent.m) | ||||||
|  |         {} | ||||||
|  | 
 | ||||||
|  |         bool is_proxy(app *k, app_ref &v); | ||||||
|  |         app* mk_proxy(expr *v); | ||||||
|  |         void reset(); | ||||||
|  |         bool is_proxy_def(expr *v); | ||||||
|  | 
 | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     friend struct def_manager; | ||||||
|  |     ast_manager &m; | ||||||
|  |     solver &m_solver; | ||||||
|  |     app_ref_vector m_proxies; | ||||||
|  |     unsigned m_num_proxies; | ||||||
|  |     vector<def_manager> m_defs; | ||||||
|  |     def_manager m_base_defs; | ||||||
|  |     expr_ref_vector m_assumptions; | ||||||
|  |     unsigned m_first_assumption; | ||||||
|  |     bool m_is_proxied; | ||||||
|  | 
 | ||||||
|  |     stopwatch m_itp_watch; | ||||||
|  | 
 | ||||||
|  |     expr_substitution m_elim_proxies_sub; | ||||||
|  |     bool m_split_literals; | ||||||
|  |     bool m_new_unsat_core; | ||||||
|  |     bool m_minimize_unsat_core; | ||||||
|  |     bool m_farkas_optimized; | ||||||
|  |     bool m_farkas_a_const; | ||||||
|  | 
 | ||||||
|  |     bool is_proxy(expr *e, app_ref &def); | ||||||
|  |     void undo_proxies_in_core(ptr_vector<expr> &v); | ||||||
|  |     app* mk_proxy(expr *v); | ||||||
|  |     app* fresh_proxy(); | ||||||
|  |     void elim_proxies(expr_ref_vector &v); | ||||||
|  | public: | ||||||
|  |     itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool split_literals = false) : | ||||||
|  |         m(solver.get_manager()), | ||||||
|  |         m_solver(solver), | ||||||
|  |         m_proxies(m), | ||||||
|  |         m_num_proxies(0), | ||||||
|  |         m_base_defs(*this), | ||||||
|  |         m_assumptions(m), | ||||||
|  |         m_first_assumption(0), | ||||||
|  |         m_is_proxied(false), | ||||||
|  |         m_elim_proxies_sub(m, false, true), | ||||||
|  |         m_split_literals(split_literals), | ||||||
|  |         m_new_unsat_core(new_unsat_core), | ||||||
|  |         m_minimize_unsat_core(minimize_unsat_core), | ||||||
|  |         m_farkas_optimized(farkas_optimized), | ||||||
|  |         m_farkas_a_const(farkas_a_const) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     virtual ~itp_solver() {} | ||||||
|  | 
 | ||||||
|  |     /* itp solver specific */ | ||||||
|  |     virtual void get_unsat_core(expr_ref_vector &core); | ||||||
|  |     virtual void get_itp_core(expr_ref_vector &core); | ||||||
|  |     void set_split_literals(bool v) {m_split_literals = v;} | ||||||
|  |     bool mk_proxies(expr_ref_vector &v, unsigned from = 0); | ||||||
|  |     void undo_proxies(expr_ref_vector &v); | ||||||
|  | 
 | ||||||
|  |     void push_bg(expr *e); | ||||||
|  |     void pop_bg(unsigned n); | ||||||
|  |     unsigned get_num_bg(); | ||||||
|  | 
 | ||||||
|  |     void get_full_unsat_core(ptr_vector<expr> &core) | ||||||
|  |     {m_solver.get_unsat_core(core);} | ||||||
|  | 
 | ||||||
|  |     /* solver interface */ | ||||||
|  | 
 | ||||||
|  |     virtual solver* translate(ast_manager &m, params_ref const &p) | ||||||
|  |     {return m_solver.translate(m, p);} | ||||||
|  |     virtual void updt_params(params_ref const &p) | ||||||
|  |     {m_solver.updt_params(p);} | ||||||
|  |     virtual void collect_param_descrs(param_descrs &r) | ||||||
|  |     {m_solver.collect_param_descrs(r);} | ||||||
|  |     virtual void set_produce_models(bool f) | ||||||
|  |     {m_solver.set_produce_models(f);} | ||||||
|  |     virtual void assert_expr(expr *t) | ||||||
|  |     {m_solver.assert_expr(t);} | ||||||
|  | 
 | ||||||
|  |     virtual void assert_expr(expr *t, expr *a) | ||||||
|  |     {NOT_IMPLEMENTED_YET();} | ||||||
|  | 
 | ||||||
|  |     virtual void push(); | ||||||
|  |     virtual void pop(unsigned n); | ||||||
|  |     virtual unsigned get_scope_level() const | ||||||
|  |     {return m_solver.get_scope_level();} | ||||||
|  | 
 | ||||||
|  |     virtual lbool check_sat(unsigned num_assumptions, expr * const *assumptions); | ||||||
|  |     virtual void set_progress_callback(progress_callback *callback) | ||||||
|  |     {m_solver.set_progress_callback(callback);} | ||||||
|  |     virtual unsigned get_num_assertions() const | ||||||
|  |     {return m_solver.get_num_assertions();} | ||||||
|  |     virtual expr * get_assertion(unsigned idx) const | ||||||
|  |     {return m_solver.get_assertion(idx);} | ||||||
|  |     virtual unsigned get_num_assumptions() const | ||||||
|  |     {return m_solver.get_num_assumptions();} | ||||||
|  |     virtual expr * get_assumption(unsigned idx) const | ||||||
|  |     {return m_solver.get_assumption(idx);} | ||||||
|  |     virtual std::ostream &display(std::ostream &out) const | ||||||
|  |     {m_solver.display(out); return out;} | ||||||
|  | 
 | ||||||
|  |     /* check_sat_result interface */ | ||||||
|  | 
 | ||||||
|  |     virtual void collect_statistics(statistics &st) const ; | ||||||
|  |     virtual void reset_statistics(); | ||||||
|  |     virtual void get_unsat_core(ptr_vector<expr> &r); | ||||||
|  |     virtual void get_model(model_ref &m) {m_solver.get_model(m);} | ||||||
|  |     virtual proof *get_proof() {return m_solver.get_proof();} | ||||||
|  |     virtual std::string reason_unknown() const | ||||||
|  |     {return m_solver.reason_unknown();} | ||||||
|  |     virtual void set_reason_unknown(char const* msg) | ||||||
|  |     {m_solver.set_reason_unknown(msg);} | ||||||
|  |     virtual void get_labels(svector<symbol> &r) | ||||||
|  |     {m_solver.get_labels(r);} | ||||||
|  |     virtual ast_manager &get_manager() const {return m;} | ||||||
|  | 
 | ||||||
|  |     virtual void refresh(); | ||||||
|  | 
 | ||||||
|  |     class scoped_mk_proxy { | ||||||
|  |         itp_solver &m_s; | ||||||
|  |         expr_ref_vector &m_v; | ||||||
|  |     public: | ||||||
|  |         scoped_mk_proxy(itp_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) | ||||||
|  |         {m_s.mk_proxies(m_v);} | ||||||
|  |         ~scoped_mk_proxy() | ||||||
|  |         {m_s.undo_proxies(m_v);} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class scoped_bg { | ||||||
|  |         itp_solver &m_s; | ||||||
|  |         unsigned m_bg_sz; | ||||||
|  |     public: | ||||||
|  |         scoped_bg(itp_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} | ||||||
|  |         ~scoped_bg() | ||||||
|  |         {if(m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										170
									
								
								src/muz/spacer/spacer_legacy_frames.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/muz/spacer/spacer_legacy_frames.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,170 @@ | ||||||
|  | /*
 | ||||||
|  |   Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  |   Legacy implementations of frames. To be removed. | ||||||
|  |  */ | ||||||
|  | #include "spacer_context.h" | ||||||
|  | #include <sstream> | ||||||
|  | #include <iomanip> | ||||||
|  | 
 | ||||||
|  | #include "dl_util.h" | ||||||
|  | #include "rewriter.h" | ||||||
|  | #include "rewriter_def.h" | ||||||
|  | #include "var_subst.h" | ||||||
|  | #include "util.h" | ||||||
|  | #include "spacer_prop_solver.h" | ||||||
|  | #include "spacer_context.h" | ||||||
|  | #include "spacer_generalizers.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "dl_rule_set.h" | ||||||
|  | #include "unit_subsumption_tactic.h" | ||||||
|  | #include "model_smt2_pp.h" | ||||||
|  | #include "dl_mk_rule_inliner.h" | ||||||
|  | #include "ast_smt2_pp.h" | ||||||
|  | #include "ast_ll_pp.h" | ||||||
|  | #include "ast_util.h" | ||||||
|  | #include "proof_checker.h" | ||||||
|  | #include "smt_value_sort.h" | ||||||
|  | #include "proof_utils.h" | ||||||
|  | #include "scoped_proof.h" | ||||||
|  | #include "spacer_qe_project.h" | ||||||
|  | #include "blast_term_ite_tactic.h" | ||||||
|  | 
 | ||||||
|  | #include "timeit.h" | ||||||
|  | #include "luby.h" | ||||||
|  | #include "expr_safe_replace.h" | ||||||
|  | #include "expr_abstract.h" | ||||||
|  | #include "obj_equiv_class.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | // ------------------
 | ||||||
|  | // legacy_frames
 | ||||||
|  | void pred_transformer::legacy_frames::simplify_formulas(tactic& tac, | ||||||
|  |         expr_ref_vector& v) | ||||||
|  | { | ||||||
|  |     ast_manager &m = m_pt.get_ast_manager(); | ||||||
|  |     goal_ref g(alloc(goal, m, false, false, false)); | ||||||
|  |     for (unsigned j = 0; j < v.size(); ++j) { g->assert_expr(v[j].get()); } | ||||||
|  |     model_converter_ref mc; | ||||||
|  |     proof_converter_ref pc; | ||||||
|  |     expr_dependency_ref core(m); | ||||||
|  |     goal_ref_buffer result; | ||||||
|  |     tac(g, result, mc, pc, core); | ||||||
|  |     SASSERT(result.size() == 1); | ||||||
|  |     goal* r = result[0]; | ||||||
|  |     v.reset(); | ||||||
|  |     for (unsigned j = 0; j < r->size(); ++j) { v.push_back(r->form(j)); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pred_transformer::legacy_frames::simplify_formulas() | ||||||
|  | { | ||||||
|  |     ast_manager &m = m_pt.get_ast_manager(); | ||||||
|  |     tactic_ref us = mk_unit_subsumption_tactic(m); | ||||||
|  |     simplify_formulas(*us, m_invariants); | ||||||
|  |     for (unsigned i = 0; i < m_levels.size(); ++i) { | ||||||
|  |         simplify_formulas(*us, m_levels[i]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pred_transformer::legacy_frames::get_frame_geq_lemmas(unsigned lvl, | ||||||
|  |         expr_ref_vector &out) | ||||||
|  | { | ||||||
|  |     get_frame_lemmas(infty_level(), out); | ||||||
|  |     for (unsigned i = lvl, sz = m_levels.size(); i < sz; ++i) | ||||||
|  |     { get_frame_lemmas(i, out); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool pred_transformer::legacy_frames::propagate_to_next_level(unsigned src_level) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     ast_manager &m = m_pt.get_ast_manager(); | ||||||
|  |     if (m_levels.size() <= src_level) { return true; } | ||||||
|  |     if (m_levels [src_level].empty()) { return true; } | ||||||
|  | 
 | ||||||
|  |     unsigned tgt_level = next_level(src_level); | ||||||
|  |     m_pt.ensure_level(next_level(tgt_level)); | ||||||
|  | 
 | ||||||
|  |     TRACE("spacer", | ||||||
|  |           tout << "propagating " << src_level << " to " << tgt_level; | ||||||
|  |           tout << " for relation " << m_pt.head()->get_name() << "\n";); | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0; i < m_levels[src_level].size();) { | ||||||
|  |         expr_ref_vector &src = m_levels[src_level]; | ||||||
|  |         expr * curr = src[i].get(); | ||||||
|  |         unsigned stored_lvl; | ||||||
|  |         VERIFY(m_prop2level.find(curr, stored_lvl)); | ||||||
|  |         SASSERT(stored_lvl >= src_level); | ||||||
|  |         unsigned solver_level; | ||||||
|  |         if (stored_lvl > src_level) { | ||||||
|  |             TRACE("spacer", tout << "at level: " << stored_lvl << " " << mk_pp(curr, m) << "\n";); | ||||||
|  |             src[i] = src.back(); | ||||||
|  |             src.pop_back(); | ||||||
|  |         } else if (m_pt.is_invariant(tgt_level, curr, solver_level)) { | ||||||
|  |             // -- might invalidate src reference
 | ||||||
|  |             add_lemma(curr, solver_level); | ||||||
|  |             TRACE("spacer", tout << "is invariant: " << pp_level(solver_level) << " " << mk_pp(curr, m) << "\n";); | ||||||
|  |             // shadow higher-level src
 | ||||||
|  |             expr_ref_vector &src = m_levels[src_level]; | ||||||
|  |             src[i] = src.back(); | ||||||
|  |             src.pop_back(); | ||||||
|  |             ++m_pt.m_stats.m_num_propagations; | ||||||
|  |         } else { | ||||||
|  |             TRACE("spacer", tout << "not propagated: " << mk_pp(curr, m) << "\n";); | ||||||
|  |             ++i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     CTRACE("spacer", m_levels[src_level].empty(), | ||||||
|  |            tout << "Fully propagated level " | ||||||
|  |            << src_level << " of " << m_pt.head()->get_name() << "\n";); | ||||||
|  | 
 | ||||||
|  |     return m_levels[src_level].empty(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool pred_transformer::legacy_frames::add_lemma(expr * lemma, unsigned lvl) | ||||||
|  | { | ||||||
|  |     if (is_infty_level(lvl)) { | ||||||
|  |         if (!m_invariants.contains(lemma)) { | ||||||
|  |             m_invariants.push_back(lemma); | ||||||
|  |             m_prop2level.insert(lemma, lvl); | ||||||
|  |         //m_pt.add_lemma_core (lemma, lvl);
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned old_level; | ||||||
|  |     if (!m_prop2level.find(lemma, old_level) || old_level < lvl) { | ||||||
|  |         m_levels[lvl].push_back(lemma); | ||||||
|  |         m_prop2level.insert(lemma, lvl); | ||||||
|  |         //m_pt.add_lemma_core (lemma, lvl);
 | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void  pred_transformer::legacy_frames::propagate_to_infinity(unsigned level) | ||||||
|  | { | ||||||
|  |     TRACE("spacer", tout << "propagating to oo from lvl " << level | ||||||
|  |           << " of " << m_pt.m_head->get_name() << "\n";); | ||||||
|  | 
 | ||||||
|  |     if (m_levels.empty()) { return; } | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = m_levels.size(); i > level; --i) { | ||||||
|  |         expr_ref_vector &lemmas = m_levels [i - 1]; | ||||||
|  |         for (unsigned j = 0; j < lemmas.size(); ++j) | ||||||
|  |         { add_lemma(lemmas.get(j), infty_level()); } | ||||||
|  |         lemmas.reset(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void pred_transformer::legacy_frames::inherit_frames(legacy_frames& other) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     SASSERT(m_pt.m_head == other.m_pt.m_head); | ||||||
|  |     obj_map<expr, unsigned>::iterator it  = other.m_prop2level.begin(); | ||||||
|  |     obj_map<expr, unsigned>::iterator end = other.m_prop2level.end(); | ||||||
|  |     for (; it != end; ++it) { add_lemma(it->m_key, it->m_value); } | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								src/muz/spacer/spacer_legacy_frames.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/muz/spacer/spacer_legacy_frames.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | /**++
 | ||||||
|  |   Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  |   Legacy implementations of frames. To be removed. | ||||||
|  | 
 | ||||||
|  |   Notes: this file is included from the middle of spacer_context.h | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | class legacy_frames | ||||||
|  | { | ||||||
|  |     pred_transformer &m_pt; | ||||||
|  | 
 | ||||||
|  |     /// level formulas
 | ||||||
|  |     vector<expr_ref_vector>      m_levels; | ||||||
|  |     /// map property to level where it occurs.
 | ||||||
|  |     obj_map<expr, unsigned>      m_prop2level; | ||||||
|  |     /// properties that are invariant.
 | ||||||
|  |     expr_ref_vector              m_invariants; | ||||||
|  | 
 | ||||||
|  |     void simplify_formulas (tactic& tac, expr_ref_vector& v); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     legacy_frames (pred_transformer &pt) : | ||||||
|  |     m_pt(pt), m_invariants (m_pt.get_ast_manager ()) {} | ||||||
|  |     pred_transformer& pt () const {return m_pt;} | ||||||
|  |     bool add_lemma (expr * lemma, unsigned level); | ||||||
|  |     void get_frame_lemmas (unsigned level, expr_ref_vector &out) | ||||||
|  |     { | ||||||
|  |         if(is_infty_level(level)) { out.append(m_invariants); } | ||||||
|  |         else if(level < m_levels.size()) { out.append(m_levels [level]); } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out); | ||||||
|  |     void add_frame () {m_levels.push_back (expr_ref_vector (m_pt.get_ast_manager ()));} | ||||||
|  | 
 | ||||||
|  |     unsigned size () const {return m_levels.size ();} | ||||||
|  |     unsigned lemma_size () const {return m_prop2level.size ();} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void propagate_to_infinity (unsigned level); | ||||||
|  |     bool propagate_to_next_level (unsigned level); | ||||||
|  | 
 | ||||||
|  |     void simplify_formulas (); | ||||||
|  | 
 | ||||||
|  |     void inherit_frames (legacy_frames& other); | ||||||
|  | 
 | ||||||
|  | }; | ||||||
							
								
								
									
										116
									
								
								src/muz/spacer/spacer_legacy_mbp.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/muz/spacer/spacer_legacy_mbp.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_legacy_mbp.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    Legacy Model Based Projection. Used by Grigory Fedyukovich | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  |     Anvesh Komuravelli | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include <sstream> | ||||||
|  | #include "arith_simplifier_plugin.h" | ||||||
|  | #include "array_decl_plugin.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | #include "basic_simplifier_plugin.h" | ||||||
|  | #include "bv_simplifier_plugin.h" | ||||||
|  | #include "bool_rewriter.h" | ||||||
|  | #include "dl_util.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "smt_params.h" | ||||||
|  | #include "model.h" | ||||||
|  | #include "ref_vector.h" | ||||||
|  | #include "rewriter.h" | ||||||
|  | #include "rewriter_def.h" | ||||||
|  | #include "util.h" | ||||||
|  | #include "spacer_manager.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "arith_decl_plugin.h" | ||||||
|  | #include "expr_replacer.h" | ||||||
|  | #include "model_smt2_pp.h" | ||||||
|  | #include "scoped_proof.h" | ||||||
|  | #include "qe_lite.h" | ||||||
|  | #include "spacer_qe_project.h" | ||||||
|  | #include "model_pp.h" | ||||||
|  | #include "expr_safe_replace.h" | ||||||
|  | 
 | ||||||
|  | #include "datatype_decl_plugin.h" | ||||||
|  | #include "bv_decl_plugin.h" | ||||||
|  | 
 | ||||||
|  | #include "spacer_legacy_mev.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map) | ||||||
|  | { | ||||||
|  |     th_rewriter rw(m); | ||||||
|  |     // qe-lite; TODO: use qe_lite aggressively
 | ||||||
|  |     params_ref p; | ||||||
|  |     qe_lite qe(m, p, true); | ||||||
|  |     qe(vars, fml); | ||||||
|  |     rw(fml); | ||||||
|  | 
 | ||||||
|  |     TRACE("spacer", | ||||||
|  |           tout << "After qe_lite:\n"; | ||||||
|  |           tout << mk_pp(fml, m) << "\n"; | ||||||
|  |           tout << "Vars:\n"; | ||||||
|  |     for (unsigned i = 0; i < vars.size(); ++i) { | ||||||
|  |     tout << mk_pp(vars.get(i), m) << "\n"; | ||||||
|  |     } | ||||||
|  |          ); | ||||||
|  | 
 | ||||||
|  |     // substitute model values for booleans and
 | ||||||
|  |     // use LW projection for arithmetic variables
 | ||||||
|  |     if (!vars.empty()) { | ||||||
|  |         app_ref_vector arith_vars(m); | ||||||
|  |         expr_substitution sub(m); | ||||||
|  |         proof_ref pr(m.mk_asserted(m.mk_true()), m); | ||||||
|  |         expr_ref bval(m); | ||||||
|  |         for (unsigned i = 0; i < vars.size(); i++) { | ||||||
|  |             if (m.is_bool(vars.get(i))) { | ||||||
|  |                 // obtain the interpretation of the ith var using model completion
 | ||||||
|  |                 VERIFY(M->eval(vars.get(i), bval, true)); | ||||||
|  |                 sub.insert(vars.get(i), bval, pr); | ||||||
|  |             } else { | ||||||
|  |                 arith_vars.push_back(vars.get(i)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!sub.empty()) { | ||||||
|  |             scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer(m); | ||||||
|  |             rep->set_substitution(&sub); | ||||||
|  |             (*rep)(fml); | ||||||
|  |             rw(fml); | ||||||
|  |             TRACE("spacer", | ||||||
|  |                   tout << "Projected Boolean vars:\n" << mk_pp(fml, m) << "\n"; | ||||||
|  |                  ); | ||||||
|  |         } | ||||||
|  |         // model based projection
 | ||||||
|  |         if (!arith_vars.empty()) { | ||||||
|  |             TRACE("spacer", | ||||||
|  |                   tout << "Arith vars:\n"; | ||||||
|  |             for (unsigned i = 0; i < arith_vars.size(); ++i) { | ||||||
|  |             tout << mk_pp(arith_vars.get(i), m) << "\n"; | ||||||
|  |             } | ||||||
|  |                  ); | ||||||
|  |             { | ||||||
|  |                 scoped_no_proof _sp(m); | ||||||
|  |                 qe::arith_project(*M, arith_vars, fml, map); | ||||||
|  |             } | ||||||
|  |             SASSERT(arith_vars.empty()); | ||||||
|  |             TRACE("spacer", | ||||||
|  |                   tout << "Projected arith vars:\n" << mk_pp(fml, m) << "\n"; | ||||||
|  |                  ); | ||||||
|  |         } | ||||||
|  |         SASSERT(M->eval(fml, bval, true) && m.is_true(bval));    // M |= fml
 | ||||||
|  |         vars.reset(); | ||||||
|  |         vars.append(arith_vars); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										837
									
								
								src/muz/spacer/spacer_legacy_mev.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										837
									
								
								src/muz/spacer/spacer_legacy_mev.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,837 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  |  Deprecated implementation of model evaluator. To be removed. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <sstream> | ||||||
|  | #include "arith_simplifier_plugin.h" | ||||||
|  | #include "array_decl_plugin.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | #include "basic_simplifier_plugin.h" | ||||||
|  | #include "bv_simplifier_plugin.h" | ||||||
|  | #include "bool_rewriter.h" | ||||||
|  | #include "dl_util.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "smt_params.h" | ||||||
|  | #include "model.h" | ||||||
|  | #include "ref_vector.h" | ||||||
|  | #include "rewriter.h" | ||||||
|  | #include "rewriter_def.h" | ||||||
|  | #include "util.h" | ||||||
|  | #include "spacer_manager.h" | ||||||
|  | #include "spacer_legacy_mev.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "arith_decl_plugin.h" | ||||||
|  | #include "expr_replacer.h" | ||||||
|  | #include "model_smt2_pp.h" | ||||||
|  | #include "scoped_proof.h" | ||||||
|  | #include "qe_lite.h" | ||||||
|  | #include "spacer_qe_project.h" | ||||||
|  | #include "model_pp.h" | ||||||
|  | #include "expr_safe_replace.h" | ||||||
|  | 
 | ||||||
|  | #include "datatype_decl_plugin.h" | ||||||
|  | #include "bv_decl_plugin.h" | ||||||
|  | 
 | ||||||
|  | namespace old { | ||||||
|  | 
 | ||||||
|  | /////////////////////////
 | ||||||
|  | // model_evaluator
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void model_evaluator::assign_value(expr* e, expr* val) | ||||||
|  | { | ||||||
|  |     rational r; | ||||||
|  |     if (m.is_true(val)) { | ||||||
|  |         set_true(e); | ||||||
|  |     } else if (m.is_false(val)) { | ||||||
|  |         set_false(e); | ||||||
|  |     } else if (m_arith.is_numeral(val, r)) { | ||||||
|  |         set_number(e, r); | ||||||
|  |     } else if (m.is_value(val)) { | ||||||
|  |         set_value(e, val); | ||||||
|  |     } else { | ||||||
|  |         IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << "\n";); | ||||||
|  |         TRACE("old_spacer", tout << "Variable is not tracked: " << mk_pp(e, m) << "\n";); | ||||||
|  |         set_x(e); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::setup_model(const model_ref& model) | ||||||
|  | { | ||||||
|  |     m_numbers.reset(); | ||||||
|  |     m_values.reset(); | ||||||
|  |     m_model = model.get(); | ||||||
|  |     rational r; | ||||||
|  |     unsigned sz = model->get_num_constants(); | ||||||
|  |     for (unsigned i = 0; i < sz; i++) { | ||||||
|  |         func_decl * d = model->get_constant(i); | ||||||
|  |         expr* val = model->get_const_interp(d); | ||||||
|  |         expr* e = m.mk_const(d); | ||||||
|  |         m_refs.push_back(e); | ||||||
|  |         assign_value(e, val); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::reset() | ||||||
|  | { | ||||||
|  |     m1.reset(); | ||||||
|  |     m2.reset(); | ||||||
|  |     m_values.reset(); | ||||||
|  |     m_visited.reset(); | ||||||
|  |     m_numbers.reset(); | ||||||
|  |     m_refs.reset(); | ||||||
|  |     m_model = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void model_evaluator::minimize_literals(ptr_vector<expr> const& formulas, | ||||||
|  |                                         const model_ref& mdl, expr_ref_vector& result) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     TRACE("old_spacer", | ||||||
|  |           tout << "formulas:\n"; | ||||||
|  |           for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n"; | ||||||
|  |          ); | ||||||
|  | 
 | ||||||
|  |     expr_ref tmp(m); | ||||||
|  |     ptr_vector<expr> tocollect; | ||||||
|  | 
 | ||||||
|  |     setup_model(mdl); | ||||||
|  |     collect(formulas, tocollect); | ||||||
|  |     for (unsigned i = 0; i < tocollect.size(); ++i) { | ||||||
|  |         expr* e = tocollect[i]; | ||||||
|  |         expr* e1, *e2; | ||||||
|  |         SASSERT(m.is_bool(e)); | ||||||
|  |         SASSERT(is_true(e) || is_false(e)); | ||||||
|  |         if (is_true(e)) { | ||||||
|  |             result.push_back(e); | ||||||
|  |         } | ||||||
|  |         // hack to break disequalities for arithmetic variables.
 | ||||||
|  |         else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) { | ||||||
|  |             if (get_number(e1) < get_number(e2)) { | ||||||
|  |                 result.push_back(m_arith.mk_lt(e1, e2)); | ||||||
|  |             } else { | ||||||
|  |                 result.push_back(m_arith.mk_lt(e2, e1)); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             result.push_back(m.mk_not(e)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     reset(); | ||||||
|  |     TRACE("old_spacer", | ||||||
|  |           tout << "minimized model:\n"; | ||||||
|  |           for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n"; | ||||||
|  |          ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect) | ||||||
|  | { | ||||||
|  |     SASSERT(m.is_bool(e)); | ||||||
|  |     SASSERT(is_true(e) || is_false(e)); | ||||||
|  |     unsigned v = is_true(e); | ||||||
|  |     unsigned sz = e->get_num_args(); | ||||||
|  |     expr* const* args = e->get_args(); | ||||||
|  |     if (e->get_family_id() == m.get_basic_family_id()) { | ||||||
|  |         switch (e->get_decl_kind()) { | ||||||
|  |         case OP_TRUE: | ||||||
|  |             break; | ||||||
|  |         case OP_FALSE: | ||||||
|  |             break; | ||||||
|  |         case OP_EQ: | ||||||
|  |         case OP_IFF: | ||||||
|  |             if (args[0] == args[1]) { | ||||||
|  |                 SASSERT(v); | ||||||
|  |                 // no-op
 | ||||||
|  |             } else if (m.is_bool(args[0])) { | ||||||
|  |                 todo.append(sz, args); | ||||||
|  |             } else { | ||||||
|  |                 tocollect.push_back(e); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case OP_DISTINCT: | ||||||
|  |             tocollect.push_back(e); | ||||||
|  |             break; | ||||||
|  |         case OP_ITE: | ||||||
|  |             if (args[1] == args[2]) { | ||||||
|  |                 tocollect.push_back(args[1]); | ||||||
|  |             } else if (is_true(args[1]) && is_true(args[2])) { | ||||||
|  |                 todo.append(2, args + 1); | ||||||
|  |             } else if (is_false(args[1]) && is_false(args[2])) { | ||||||
|  |                 todo.append(2, args + 1); | ||||||
|  |             } else if (is_true(args[0])) { | ||||||
|  |                 todo.append(2, args); | ||||||
|  |             } else { | ||||||
|  |                 SASSERT(is_false(args[0])); | ||||||
|  |                 todo.push_back(args[0]); | ||||||
|  |                 todo.push_back(args[2]); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case OP_AND: | ||||||
|  |             if (v) { | ||||||
|  |                 todo.append(sz, args); | ||||||
|  |             } else { | ||||||
|  |                 unsigned i = 0; | ||||||
|  |                 for (; !is_false(args[i]) && i < sz; ++i); | ||||||
|  |                 if (i == sz) { | ||||||
|  |                     fatal_error(1); | ||||||
|  |                 } | ||||||
|  |                 VERIFY(i < sz); | ||||||
|  |                 todo.push_back(args[i]); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case OP_OR: | ||||||
|  |             if (v) { | ||||||
|  |                 unsigned i = 0; | ||||||
|  |                 for (; !is_true(args[i]) && i < sz; ++i); | ||||||
|  |                 if (i == sz) { | ||||||
|  |                     fatal_error(1); | ||||||
|  |                 } | ||||||
|  |                 VERIFY(i < sz); | ||||||
|  |                 todo.push_back(args[i]); | ||||||
|  |             } else { | ||||||
|  |                 todo.append(sz, args); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case OP_XOR: | ||||||
|  |         case OP_NOT: | ||||||
|  |             todo.append(sz, args); | ||||||
|  |             break; | ||||||
|  |         case OP_IMPLIES: | ||||||
|  |             if (v) { | ||||||
|  |                 if (is_true(args[1])) { | ||||||
|  |                     todo.push_back(args[1]); | ||||||
|  |                 } else if (is_false(args[0])) { | ||||||
|  |                     todo.push_back(args[0]); | ||||||
|  |                 } else { | ||||||
|  |                     IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); | ||||||
|  |                     UNREACHABLE(); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 todo.append(sz, args); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); | ||||||
|  |             UNREACHABLE(); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         tocollect.push_back(e); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect) | ||||||
|  | { | ||||||
|  |     ptr_vector<expr> todo; | ||||||
|  |     todo.append(formulas); | ||||||
|  |     m_visited.reset(); | ||||||
|  | 
 | ||||||
|  |     VERIFY(check_model(formulas)); | ||||||
|  | 
 | ||||||
|  |     while (!todo.empty()) { | ||||||
|  |         app*  e = to_app(todo.back()); | ||||||
|  |         todo.pop_back(); | ||||||
|  |         if (!m_visited.is_marked(e)) { | ||||||
|  |             process_formula(e, todo, tocollect); | ||||||
|  |             m_visited.mark(e, true); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     m_visited.reset(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::eval_arith(app* e) | ||||||
|  | { | ||||||
|  |     rational r, r2; | ||||||
|  | 
 | ||||||
|  | #define ARG1 e->get_arg(0) | ||||||
|  | #define ARG2 e->get_arg(1) | ||||||
|  | 
 | ||||||
|  |     unsigned arity = e->get_num_args(); | ||||||
|  |     for (unsigned i = 0; i < arity; ++i) { | ||||||
|  |         expr* arg = e->get_arg(i); | ||||||
|  |         if (is_x(arg)) { | ||||||
|  |             set_x(e); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         SASSERT(!is_unknown(arg)); | ||||||
|  |     } | ||||||
|  |     switch (e->get_decl_kind()) { | ||||||
|  |     case OP_NUM: | ||||||
|  |         VERIFY(m_arith.is_numeral(e, r)); | ||||||
|  |         set_number(e, r); | ||||||
|  |         break; | ||||||
|  |     case OP_IRRATIONAL_ALGEBRAIC_NUM: | ||||||
|  |         set_x(e); | ||||||
|  |         break; | ||||||
|  |     case OP_LE: | ||||||
|  |         set_bool(e, get_number(ARG1) <= get_number(ARG2)); | ||||||
|  |         break; | ||||||
|  |     case OP_GE: | ||||||
|  |         set_bool(e, get_number(ARG1) >= get_number(ARG2)); | ||||||
|  |         break; | ||||||
|  |     case OP_LT: | ||||||
|  |         set_bool(e, get_number(ARG1) < get_number(ARG2)); | ||||||
|  |         break; | ||||||
|  |     case OP_GT: | ||||||
|  |         set_bool(e, get_number(ARG1) > get_number(ARG2)); | ||||||
|  |         break; | ||||||
|  |     case OP_ADD: | ||||||
|  |         r = rational::zero(); | ||||||
|  |         for (unsigned i = 0; i < arity; ++i) { | ||||||
|  |             r += get_number(e->get_arg(i)); | ||||||
|  |         } | ||||||
|  |         set_number(e, r); | ||||||
|  |         break; | ||||||
|  |     case OP_SUB: | ||||||
|  |         r = get_number(e->get_arg(0)); | ||||||
|  |         for (unsigned i = 1; i < arity; ++i) { | ||||||
|  |             r -= get_number(e->get_arg(i)); | ||||||
|  |         } | ||||||
|  |         set_number(e, r); | ||||||
|  |         break; | ||||||
|  |     case OP_UMINUS: | ||||||
|  |         SASSERT(arity == 1); | ||||||
|  |         set_number(e, -get_number(e->get_arg(0))); | ||||||
|  |         break; | ||||||
|  |     case OP_MUL: | ||||||
|  |         r = rational::one(); | ||||||
|  |         for (unsigned i = 0; i < arity; ++i) { | ||||||
|  |             r *= get_number(e->get_arg(i)); | ||||||
|  |         } | ||||||
|  |         set_number(e, r); | ||||||
|  |         break; | ||||||
|  |     case OP_DIV: | ||||||
|  |         SASSERT(arity == 2); | ||||||
|  |         r = get_number(ARG2); | ||||||
|  |         if (r.is_zero()) { | ||||||
|  |             set_x(e); | ||||||
|  |         } else { | ||||||
|  |             set_number(e, get_number(ARG1) / r); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_IDIV: | ||||||
|  |         SASSERT(arity == 2); | ||||||
|  |         r = get_number(ARG2); | ||||||
|  |         if (r.is_zero()) { | ||||||
|  |             set_x(e); | ||||||
|  |         } else { | ||||||
|  |             set_number(e, div(get_number(ARG1), r)); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_REM: | ||||||
|  |         // rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2)
 | ||||||
|  |         SASSERT(arity == 2); | ||||||
|  |         r = get_number(ARG2); | ||||||
|  |         if (r.is_zero()) { | ||||||
|  |             set_x(e); | ||||||
|  |         } else { | ||||||
|  |             r2 = mod(get_number(ARG1), r); | ||||||
|  |             if (r.is_neg()) { r2.neg(); } | ||||||
|  |             set_number(e, r2); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_MOD: | ||||||
|  |         SASSERT(arity == 2); | ||||||
|  |         r = get_number(ARG2); | ||||||
|  |         if (r.is_zero()) { | ||||||
|  |             set_x(e); | ||||||
|  |         } else { | ||||||
|  |             set_number(e, mod(get_number(ARG1), r)); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_TO_REAL: | ||||||
|  |         SASSERT(arity == 1); | ||||||
|  |         set_number(e, get_number(ARG1)); | ||||||
|  |         break; | ||||||
|  |     case OP_TO_INT: | ||||||
|  |         SASSERT(arity == 1); | ||||||
|  |         set_number(e, floor(get_number(ARG1))); | ||||||
|  |         break; | ||||||
|  |     case OP_IS_INT: | ||||||
|  |         SASSERT(arity == 1); | ||||||
|  |         set_bool(e, get_number(ARG1).is_int()); | ||||||
|  |         break; | ||||||
|  |     case OP_POWER: | ||||||
|  |         set_x(e); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); | ||||||
|  |         UNREACHABLE(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::inherit_value(expr* e, expr* v) | ||||||
|  | { | ||||||
|  |     expr* w; | ||||||
|  |     SASSERT(!is_unknown(v)); | ||||||
|  |     SASSERT(m.get_sort(e) == m.get_sort(v)); | ||||||
|  |     if (is_x(v)) { | ||||||
|  |         set_x(e); | ||||||
|  |     } else if (m.is_bool(e)) { | ||||||
|  |         SASSERT(m.is_bool(v)); | ||||||
|  |         if (is_true(v)) { set_true(e); } | ||||||
|  |         else if (is_false(v)) { set_false(e); } | ||||||
|  |         else { | ||||||
|  |             TRACE("old_spacer", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |     } else if (m_arith.is_int_real(e)) { | ||||||
|  |         set_number(e, get_number(v)); | ||||||
|  |     } else if (m.is_value(v)) { | ||||||
|  |         set_value(e, v); | ||||||
|  |     } else if (m_values.find(v, w)) { | ||||||
|  |         set_value(e, w); | ||||||
|  |     } else { | ||||||
|  |         TRACE("old_spacer", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";); | ||||||
|  |         set_x(e); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::eval_exprs(expr_ref_vector& es) | ||||||
|  | { | ||||||
|  |     model_ref mr(m_model); | ||||||
|  |     for (unsigned j = 0; j < es.size(); ++j) { | ||||||
|  |         if (m_array.is_as_array(es[j].get())) { | ||||||
|  |             es[j] = eval(mr, es[j].get()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool model_evaluator::extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case) | ||||||
|  | { | ||||||
|  |     SASSERT(m_array.is_array(a)); | ||||||
|  | 
 | ||||||
|  |     TRACE("old_spacer", tout << mk_pp(a, m) << "\n";); | ||||||
|  |     while (m_array.is_store(a)) { | ||||||
|  |         expr_ref_vector store(m); | ||||||
|  |         store.append(to_app(a)->get_num_args() - 1, to_app(a)->get_args() + 1); | ||||||
|  |         eval_exprs(store); | ||||||
|  |         stores.push_back(store); | ||||||
|  |         a = to_app(a)->get_arg(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m_array.is_const(a)) { | ||||||
|  |         else_case = to_app(a)->get_arg(0); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while (m_array.is_as_array(a)) { | ||||||
|  |         func_decl* f = m_array.get_as_array_func_decl(to_app(a)); | ||||||
|  |         func_interp* g = m_model->get_func_interp(f); | ||||||
|  |         unsigned sz = g->num_entries(); | ||||||
|  |         unsigned arity = f->get_arity(); | ||||||
|  |         for (unsigned i = 0; i < sz; ++i) { | ||||||
|  |             expr_ref_vector store(m); | ||||||
|  |             func_entry const* fe = g->get_entry(i); | ||||||
|  |             store.append(arity, fe->get_args()); | ||||||
|  |             store.push_back(fe->get_result()); | ||||||
|  |             for (unsigned j = 0; j < store.size(); ++j) { | ||||||
|  |                 if (!is_ground(store[j].get())) { | ||||||
|  |                     TRACE("old_spacer", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             eval_exprs(store); | ||||||
|  |             stores.push_back(store); | ||||||
|  |         } | ||||||
|  |         else_case = g->get_else(); | ||||||
|  |         if (!else_case) { | ||||||
|  |             TRACE("old_spacer", tout << "no else case " << mk_pp(a, m) << "\n";); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (!is_ground(else_case)) { | ||||||
|  |             TRACE("old_spacer", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (m_array.is_as_array(else_case)) { | ||||||
|  |             model_ref mr(m_model); | ||||||
|  |             else_case = eval(mr, else_case); | ||||||
|  |         } | ||||||
|  |         TRACE("old_spacer", tout << "else case: " << mk_pp(else_case, m) << "\n";); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     TRACE("old_spacer", tout << "no translation: " << mk_pp(a, m) << "\n";); | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |    best effort evaluator of extensional array equality. | ||||||
|  | */ | ||||||
|  | void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) | ||||||
|  | { | ||||||
|  |     TRACE("old_spacer", tout << "array equality: " << mk_pp(e, m) << "\n";); | ||||||
|  |     expr_ref v1(m), v2(m); | ||||||
|  |     m_model->eval(arg1, v1); | ||||||
|  |     m_model->eval(arg2, v2); | ||||||
|  |     if (v1 == v2) { | ||||||
|  |         set_true(e); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     sort* s = m.get_sort(arg1); | ||||||
|  |     sort* r = get_array_range(s); | ||||||
|  |     // give up evaluating finite domain/range arrays
 | ||||||
|  |     if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { | ||||||
|  |         TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |         set_x(e); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     vector<expr_ref_vector> store; | ||||||
|  |     expr_ref else1(m), else2(m); | ||||||
|  |     if (!extract_array_func_interp(v1, store, else1) || | ||||||
|  |             !extract_array_func_interp(v2, store, else2)) { | ||||||
|  |         TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |         set_x(e); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (else1 != else2) { | ||||||
|  |         if (m.is_value(else1) && m.is_value(else2)) { | ||||||
|  |             TRACE("old_spacer", tout | ||||||
|  |                   << "defaults are different: " << mk_pp(e, m) << " " | ||||||
|  |                   << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); | ||||||
|  |             set_false(e); | ||||||
|  |         } else if (m_array.is_array(else1)) { | ||||||
|  |             eval_array_eq(e, else1, else2); | ||||||
|  |         } else { | ||||||
|  |             TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     expr_ref s1(m), s2(m), w1(m), w2(m); | ||||||
|  |     expr_ref_vector args1(m), args2(m); | ||||||
|  |     args1.push_back(v1); | ||||||
|  |     args2.push_back(v2); | ||||||
|  |     for (unsigned i = 0; i < store.size(); ++i) { | ||||||
|  |         args1.resize(1); | ||||||
|  |         args2.resize(1); | ||||||
|  |         args1.append(store[i].size() - 1, store[i].c_ptr()); | ||||||
|  |         args2.append(store[i].size() - 1, store[i].c_ptr()); | ||||||
|  |         s1 = m_array.mk_select(args1.size(), args1.c_ptr()); | ||||||
|  |         s2 = m_array.mk_select(args2.size(), args2.c_ptr()); | ||||||
|  |         m_model->eval(s1, w1); | ||||||
|  |         m_model->eval(s2, w2); | ||||||
|  |         if (w1 == w2) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (m.is_value(w1) && m.is_value(w2)) { | ||||||
|  |             TRACE("old_spacer", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; | ||||||
|  |                   tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; | ||||||
|  |                   tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); | ||||||
|  |             set_false(e); | ||||||
|  |         } else if (m_array.is_array(w1)) { | ||||||
|  |             eval_array_eq(e, w1, w2); | ||||||
|  |             if (is_true(e)) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     set_true(e); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::eval_eq(app* e, expr* arg1, expr* arg2) | ||||||
|  | { | ||||||
|  |     if (arg1 == arg2) { | ||||||
|  |         set_true(e); | ||||||
|  |     } else if (m_array.is_array(arg1)) { | ||||||
|  |         eval_array_eq(e, arg1, arg2); | ||||||
|  |     } else if (is_x(arg1) || is_x(arg2)) { | ||||||
|  |         set_x(e); | ||||||
|  |     } else if (m.is_bool(arg1)) { | ||||||
|  |         bool val = is_true(arg1) == is_true(arg2); | ||||||
|  |         SASSERT(val == (is_false(arg1) == is_false(arg2))); | ||||||
|  |         if (val) { | ||||||
|  |             set_true(e); | ||||||
|  |         } else { | ||||||
|  |             set_false(e); | ||||||
|  |         } | ||||||
|  |     } else if (m_arith.is_int_real(arg1)) { | ||||||
|  |         set_bool(e, get_number(arg1) == get_number(arg2)); | ||||||
|  |     } else { | ||||||
|  |         expr* e1 = get_value(arg1); | ||||||
|  |         expr* e2 = get_value(arg2); | ||||||
|  |         if (m.is_value(e1) && m.is_value(e2)) { | ||||||
|  |             set_bool(e, e1 == e2); | ||||||
|  |         } else if (e1 == e2) { | ||||||
|  |             set_bool(e, true); | ||||||
|  |         } else { | ||||||
|  |             TRACE("old_spacer", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";); | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::eval_basic(app* e) | ||||||
|  | { | ||||||
|  |     expr* arg1, *arg2; | ||||||
|  |     expr *argCond, *argThen, *argElse, *arg; | ||||||
|  |     bool has_x = false; | ||||||
|  |     unsigned arity = e->get_num_args(); | ||||||
|  |     switch (e->get_decl_kind()) { | ||||||
|  |     case OP_AND: | ||||||
|  |         for (unsigned j = 0; j < arity; ++j) { | ||||||
|  |             expr * arg = e->get_arg(j); | ||||||
|  |             if (is_false(arg)) { | ||||||
|  |                 set_false(e); | ||||||
|  |                 return; | ||||||
|  |             } else if (is_x(arg)) { | ||||||
|  |                 has_x = true; | ||||||
|  |             } else { | ||||||
|  |                 SASSERT(is_true(arg)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (has_x) { | ||||||
|  |             set_x(e); | ||||||
|  |         } else { | ||||||
|  |             set_true(e); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_OR: | ||||||
|  |         for (unsigned j = 0; j < arity; ++j) { | ||||||
|  |             expr * arg = e->get_arg(j); | ||||||
|  |             if (is_true(arg)) { | ||||||
|  |                 set_true(e); | ||||||
|  |                 return; | ||||||
|  |             } else if (is_x(arg)) { | ||||||
|  |                 has_x = true; | ||||||
|  |             } else { | ||||||
|  |                 SASSERT(is_false(arg)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (has_x) { | ||||||
|  |             set_x(e); | ||||||
|  |         } else { | ||||||
|  |             set_false(e); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_NOT: | ||||||
|  |         VERIFY(m.is_not(e, arg)); | ||||||
|  |         if (is_true(arg)) { | ||||||
|  |             set_false(e); | ||||||
|  |         } else if (is_false(arg)) { | ||||||
|  |             set_true(e); | ||||||
|  |         } else { | ||||||
|  |             SASSERT(is_x(arg)); | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_IMPLIES: | ||||||
|  |         VERIFY(m.is_implies(e, arg1, arg2)); | ||||||
|  |         if (is_false(arg1) || is_true(arg2)) { | ||||||
|  |             set_true(e); | ||||||
|  |         } else if (arg1 == arg2) { | ||||||
|  |             set_true(e); | ||||||
|  |         } else if (is_true(arg1) && is_false(arg2)) { | ||||||
|  |             set_false(e); | ||||||
|  |         } else { | ||||||
|  |             SASSERT(is_x(arg1) || is_x(arg2)); | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_IFF: | ||||||
|  |         VERIFY(m.is_iff(e, arg1, arg2)); | ||||||
|  |         eval_eq(e, arg1, arg2); | ||||||
|  |         break; | ||||||
|  |     case OP_XOR: | ||||||
|  |         VERIFY(m.is_xor(e, arg1, arg2)); | ||||||
|  |         eval_eq(e, arg1, arg2); | ||||||
|  |         if (is_false(e)) { set_true(e); } | ||||||
|  |         else if (is_true(e)) { set_false(e); } | ||||||
|  |         break; | ||||||
|  |     case OP_ITE: | ||||||
|  |         VERIFY(m.is_ite(e, argCond, argThen, argElse)); | ||||||
|  |         if (is_true(argCond)) { | ||||||
|  |             inherit_value(e, argThen); | ||||||
|  |         } else if (is_false(argCond)) { | ||||||
|  |             inherit_value(e, argElse); | ||||||
|  |         } else if (argThen == argElse) { | ||||||
|  |             inherit_value(e, argThen); | ||||||
|  |         } else if (m.is_bool(e)) { | ||||||
|  |             SASSERT(is_x(argCond)); | ||||||
|  |             if (is_x(argThen) || is_x(argElse)) { | ||||||
|  |                 set_x(e); | ||||||
|  |             } else if (is_true(argThen) == is_true(argElse)) { | ||||||
|  |                 inherit_value(e, argThen); | ||||||
|  |             } else { | ||||||
|  |                 set_x(e); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             set_x(e); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case OP_TRUE: | ||||||
|  |         set_true(e); | ||||||
|  |         break; | ||||||
|  |     case OP_FALSE: | ||||||
|  |         set_false(e); | ||||||
|  |         break; | ||||||
|  |     case OP_EQ: | ||||||
|  |         VERIFY(m.is_eq(e, arg1, arg2)); | ||||||
|  |         eval_eq(e, arg1, arg2); | ||||||
|  |         break; | ||||||
|  |     case OP_DISTINCT: { | ||||||
|  |         vector<rational> values; | ||||||
|  |         for (unsigned i = 0; i < arity; ++i) { | ||||||
|  |             expr* arg = e->get_arg(i); | ||||||
|  |             if (is_x(arg)) { | ||||||
|  |                 set_x(e); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             values.push_back(get_number(arg)); | ||||||
|  |         } | ||||||
|  |         std::sort(values.begin(), values.end()); | ||||||
|  |         for (unsigned i = 0; i + 1 < values.size(); ++i) { | ||||||
|  |             if (values[i] == values[i + 1]) { | ||||||
|  |                 set_false(e); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         set_true(e); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |         IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";); | ||||||
|  |         UNREACHABLE(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator::eval_fmls(ptr_vector<expr> const& formulas) | ||||||
|  | { | ||||||
|  |     ptr_vector<expr> todo(formulas); | ||||||
|  | 
 | ||||||
|  |     while (!todo.empty()) { | ||||||
|  |         expr * curr_e = todo.back(); | ||||||
|  | 
 | ||||||
|  |         if (!is_app(curr_e)) { | ||||||
|  |             todo.pop_back(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         app * curr = to_app(curr_e); | ||||||
|  | 
 | ||||||
|  |         if (!is_unknown(curr)) { | ||||||
|  |             todo.pop_back(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         unsigned arity = curr->get_num_args(); | ||||||
|  |         for (unsigned i = 0; i < arity; ++i) { | ||||||
|  |             if (is_unknown(curr->get_arg(i))) { | ||||||
|  |                 todo.push_back(curr->get_arg(i)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (todo.back() != curr) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         todo.pop_back(); | ||||||
|  |         if (curr->get_family_id() == m_arith.get_family_id()) { | ||||||
|  |             eval_arith(curr); | ||||||
|  |         } else if (curr->get_family_id() == m.get_basic_family_id()) { | ||||||
|  |             eval_basic(curr); | ||||||
|  |         } else { | ||||||
|  |             expr_ref vl(m); | ||||||
|  |             m_model->eval(curr, vl); | ||||||
|  |             assign_value(curr, vl); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         IF_VERBOSE(35, verbose_stream() << "assigned " << mk_pp(curr_e, m) | ||||||
|  |                    << (is_true(curr_e) ? "true" : is_false(curr_e) ? "false" : "unknown") << "\n";); | ||||||
|  |         SASSERT(!is_unknown(curr)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool model_evaluator::check_model(ptr_vector<expr> const& formulas) | ||||||
|  | { | ||||||
|  |     eval_fmls(formulas); | ||||||
|  |     bool has_x = false; | ||||||
|  |     for (unsigned i = 0; i < formulas.size(); ++i) { | ||||||
|  |         expr * form = formulas[i]; | ||||||
|  |         SASSERT(!is_unknown(form)); | ||||||
|  |         TRACE("spacer_verbose", | ||||||
|  |               tout << "formula is " << (is_true(form) ? "true" : is_false(form) ? "false" : "unknown") << "\n" << mk_pp(form, m) << "\n";); | ||||||
|  | 
 | ||||||
|  |         if (is_false(form)) { | ||||||
|  |             IF_VERBOSE(0, verbose_stream() << "formula false in model: " << mk_pp(form, m) << "\n";); | ||||||
|  |             UNREACHABLE(); | ||||||
|  |         } | ||||||
|  |         if (is_x(form)) { | ||||||
|  |             IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";); | ||||||
|  |             TRACE("old_spacer", model_smt2_pp(tout, m, *m_model, 0);); | ||||||
|  |             has_x = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return !has_x; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref model_evaluator::eval_heavy(const model_ref& model, expr* fml) | ||||||
|  | { | ||||||
|  |     expr_ref result(model->get_manager()); | ||||||
|  | 
 | ||||||
|  |     setup_model(model); | ||||||
|  |     ptr_vector<expr> fmls; | ||||||
|  |     fmls.push_back(fml); | ||||||
|  |     eval_fmls(fmls); | ||||||
|  |     if (is_false(fml)) { | ||||||
|  |         result = m.mk_false(); | ||||||
|  |     } else if (is_true(fml) || is_x(fml)) { | ||||||
|  |         result = m.mk_true(); | ||||||
|  |     } else if (m_arith.is_int_real(fml)) { | ||||||
|  |         result = m_arith.mk_numeral(get_number(fml), m_arith.is_int(fml)); | ||||||
|  |     } else { | ||||||
|  |         result = get_value(fml); | ||||||
|  |     } | ||||||
|  |     reset(); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref model_evaluator::eval(const model_ref& model, func_decl* d) | ||||||
|  | { | ||||||
|  |     SASSERT(d->get_arity() == 0); | ||||||
|  |     expr_ref result(m); | ||||||
|  |     if (m_array.is_array(d->get_range())) { | ||||||
|  |         expr_ref e(m); | ||||||
|  |         e = m.mk_const(d); | ||||||
|  |         result = eval(model, e); | ||||||
|  |     } else { | ||||||
|  |         result = model->get_const_interp(d); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref model_evaluator::eval(const model_ref& model, expr* e) | ||||||
|  | { | ||||||
|  |     expr_ref result(m); | ||||||
|  |     m_model = model.get(); | ||||||
|  |     VERIFY(m_model->eval(e, result, true)); | ||||||
|  |     if (m_array.is_array(e)) { | ||||||
|  |         vector<expr_ref_vector> stores; | ||||||
|  |         expr_ref_vector args(m); | ||||||
|  |         expr_ref else_case(m); | ||||||
|  |         if (extract_array_func_interp(result, stores, else_case)) { | ||||||
|  |             result = m_array.mk_const_array(m.get_sort(e), else_case); | ||||||
|  |             while (!stores.empty() && stores.back().back() == else_case) { | ||||||
|  |                 stores.pop_back(); | ||||||
|  |             } | ||||||
|  |             for (unsigned i = stores.size(); i > 0;) { | ||||||
|  |                 --i; | ||||||
|  |                 args.resize(1); | ||||||
|  |                 args[0] = result; | ||||||
|  |                 args.append(stores[i]); | ||||||
|  |                 result = m_array.mk_store(args.size(), args.c_ptr()); | ||||||
|  |             } | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										117
									
								
								src/muz/spacer/spacer_legacy_mev.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/muz/spacer/spacer_legacy_mev.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  |  Deprecated implementation of model evaluator. To be removed. | ||||||
|  | */ | ||||||
|  | #ifndef OLD_MEV_H | ||||||
|  | #define OLD_MEV_H | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | #include "obj_hashtable.h" | ||||||
|  | #include "ref_vector.h" | ||||||
|  | #include "simplifier.h" | ||||||
|  | #include "trace.h" | ||||||
|  | #include "vector.h" | ||||||
|  | #include "arith_decl_plugin.h" | ||||||
|  | #include "array_decl_plugin.h" | ||||||
|  | #include "bv_decl_plugin.h" | ||||||
|  | 
 | ||||||
|  | namespace old { | ||||||
|  | class model_evaluator { | ||||||
|  |     ast_manager&           m; | ||||||
|  |     arith_util             m_arith; | ||||||
|  |     array_util             m_array; | ||||||
|  |     obj_map<expr, rational> m_numbers; | ||||||
|  |     expr_ref_vector        m_refs; | ||||||
|  |     obj_map<expr, expr*>   m_values; | ||||||
|  |     model_ref              m_model; | ||||||
|  | 
 | ||||||
|  |     //00 -- non-visited
 | ||||||
|  |     //01 -- X
 | ||||||
|  |     //10 -- false
 | ||||||
|  |     //11 -- true
 | ||||||
|  |     expr_mark      m1; | ||||||
|  |     expr_mark      m2; | ||||||
|  | 
 | ||||||
|  |     /// used by collect()
 | ||||||
|  |     expr_mark      m_visited; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void reset(); | ||||||
|  | 
 | ||||||
|  |     /// caches the values of all constants in the given model
 | ||||||
|  |     void setup_model(const model_ref& model); | ||||||
|  |     /// caches the value of an expression
 | ||||||
|  |     void assign_value(expr* e, expr* v); | ||||||
|  | 
 | ||||||
|  |     /// extracts an implicant of the conjunction of formulas
 | ||||||
|  |     void collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect); | ||||||
|  | 
 | ||||||
|  |     /// one-round of extracting an implicant of e. The implicant
 | ||||||
|  |     /// literals are stored in tocollect. The worklist is stored in todo
 | ||||||
|  |     void process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect); | ||||||
|  |     void eval_arith(app* e); | ||||||
|  |     void eval_basic(app* e); | ||||||
|  |     void eval_eq(app* e, expr* arg1, expr* arg2); | ||||||
|  |     void eval_array_eq(app* e, expr* arg1, expr* arg2); | ||||||
|  |     void inherit_value(expr* e, expr* v); | ||||||
|  | 
 | ||||||
|  |     bool is_unknown(expr* x)  { return !m1.is_marked(x) && !m2.is_marked(x); } | ||||||
|  |     void set_unknown(expr* x)  { m1.mark(x, false); m2.mark(x, false); } | ||||||
|  |     bool is_x(expr* x)  { return !m1.is_marked(x) && m2.is_marked(x); } | ||||||
|  |     bool is_false(expr* x)  { return m1.is_marked(x) && !m2.is_marked(x); } | ||||||
|  |     bool is_true(expr* x)  { return m1.is_marked(x) && m2.is_marked(x); } | ||||||
|  |     void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); } | ||||||
|  |     void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } | ||||||
|  |     void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); } | ||||||
|  |     void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); } | ||||||
|  |     void set_bool(expr* x, bool v) { if(v) { set_true(x); } else { set_false(x); } } | ||||||
|  |     rational const& get_number(expr* x) const { return m_numbers.find(x); } | ||||||
|  |     void set_number(expr* x, rational const& v) | ||||||
|  |     { | ||||||
|  |         set_v(x); | ||||||
|  |         m_numbers.insert(x, v); | ||||||
|  |         TRACE("spacer_verbose", tout << mk_pp(x, m) << " " << v << "\n";); | ||||||
|  |     } | ||||||
|  |     expr* get_value(expr* x) { return m_values.find(x); } | ||||||
|  |     void set_value(expr* x, expr* v) | ||||||
|  |     { set_v(x); m_refs.push_back(v); m_values.insert(x, v); } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /// evaluates all sub-formulas and terms of the input in the current model.
 | ||||||
|  |     /// Caches the result
 | ||||||
|  |     void eval_fmls(ptr_vector<expr> const & formulas); | ||||||
|  | 
 | ||||||
|  |     /// calls eval_fmls(). Then checks whether all formulas are
 | ||||||
|  |     /// TRUE. Returns false if at lest one formula is unknown (X)
 | ||||||
|  |     bool check_model(ptr_vector<expr> const & formulas); | ||||||
|  | 
 | ||||||
|  |     bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, | ||||||
|  |                                    expr_ref& else_case); | ||||||
|  | 
 | ||||||
|  |     void eval_exprs(expr_ref_vector& es); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        \brief extract literals from formulas that satisfy formulas. | ||||||
|  | 
 | ||||||
|  |        \pre model satisfies formulas | ||||||
|  |     */ | ||||||
|  |     void minimize_literals(ptr_vector<expr> const & formulas, const model_ref& mdl, | ||||||
|  |                            expr_ref_vector& result); | ||||||
|  | 
 | ||||||
|  |     expr_ref eval_heavy(const model_ref& mdl, expr* fml); | ||||||
|  | 
 | ||||||
|  |     expr_ref eval(const model_ref& mdl, expr* e); | ||||||
|  |     expr_ref eval(const model_ref& mdl, func_decl* d); | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* OLD_MEV_H */ | ||||||
							
								
								
									
										386
									
								
								src/muz/spacer/spacer_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								src/muz/spacer/spacer_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,386 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_manager.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     A manager class for SPACER, taking care of creating of AST | ||||||
|  |     objects and conversions between them. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-8-25. | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include <sstream> | ||||||
|  | #include "spacer_manager.h" | ||||||
|  | #include "ast_smt2_pp.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "has_free_vars.h" | ||||||
|  | #include "expr_replacer.h" | ||||||
|  | #include "expr_abstract.h" | ||||||
|  | #include "model2expr.h" | ||||||
|  | #include "model_smt2_pp.h" | ||||||
|  | #include "model_converter.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class collect_decls_proc { | ||||||
|  |     func_decl_set& m_bound_decls; | ||||||
|  |     func_decl_set& m_aux_decls; | ||||||
|  | public: | ||||||
|  |     collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls): | ||||||
|  |         m_bound_decls(bound_decls), | ||||||
|  |         m_aux_decls(aux_decls) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void operator()(app* a) | ||||||
|  |     { | ||||||
|  |         if (a->get_family_id() == null_family_id) { | ||||||
|  |             func_decl* f = a->get_decl(); | ||||||
|  |             if (!m_bound_decls.contains(f)) { | ||||||
|  |                 m_aux_decls.insert(f); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator()(var* v) {} | ||||||
|  |     void operator()(quantifier* q) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set; | ||||||
|  | 
 | ||||||
|  | expr_ref inductive_property::fixup_clause(expr* fml) const | ||||||
|  | { | ||||||
|  |     expr_ref_vector disjs(m); | ||||||
|  |     flatten_or(fml, disjs); | ||||||
|  |     expr_ref result(m); | ||||||
|  |     bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref inductive_property::fixup_clauses(expr* fml) const | ||||||
|  | { | ||||||
|  |     expr_ref_vector conjs(m); | ||||||
|  |     expr_ref result(m); | ||||||
|  |     flatten_and(fml, conjs); | ||||||
|  |     for (unsigned i = 0; i < conjs.size(); ++i) { | ||||||
|  |         conjs[i] = fixup_clause(conjs[i].get()); | ||||||
|  |     } | ||||||
|  |     bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string inductive_property::to_string() const | ||||||
|  | { | ||||||
|  |     std::stringstream stm; | ||||||
|  |     model_ref md; | ||||||
|  |     expr_ref result(m); | ||||||
|  |     to_model(md); | ||||||
|  |     model_smt2_pp(stm, m, *md.get(), 0); | ||||||
|  |     return stm.str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void inductive_property::to_model(model_ref& md) const | ||||||
|  | { | ||||||
|  |     md = alloc(model, m); | ||||||
|  |     vector<relation_info> const& rs = m_relation_info; | ||||||
|  |     expr_ref_vector conjs(m); | ||||||
|  |     for (unsigned i = 0; i < rs.size(); ++i) { | ||||||
|  |         relation_info ri(rs[i]); | ||||||
|  |         func_decl * pred = ri.m_pred; | ||||||
|  |         expr_ref prop = fixup_clauses(ri.m_body); | ||||||
|  |         func_decl_ref_vector const& sig = ri.m_vars; | ||||||
|  |         expr_ref q(m); | ||||||
|  |         expr_ref_vector sig_vars(m); | ||||||
|  |         for (unsigned j = 0; j < sig.size(); ++j) { | ||||||
|  |             sig_vars.push_back(m.mk_const(sig[sig.size() - j - 1])); | ||||||
|  |         } | ||||||
|  |         expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); | ||||||
|  |         if (sig.empty()) { | ||||||
|  |             md->register_decl(pred, q); | ||||||
|  |         } else { | ||||||
|  |             func_interp* fi = alloc(func_interp, m, sig.size()); | ||||||
|  |             fi->set_else(q); | ||||||
|  |             md->register_decl(pred, fi); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     TRACE("spacer", model_smt2_pp(tout, m, *md, 0);); | ||||||
|  |     apply(const_cast<model_converter_ref&>(m_mc), md, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref inductive_property::to_expr() const | ||||||
|  | { | ||||||
|  |     model_ref md; | ||||||
|  |     expr_ref result(m); | ||||||
|  |     to_model(md); | ||||||
|  |     model2expr(md, result); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void inductive_property::display(datalog::rule_manager& rm, ptr_vector<datalog::rule> const& rules, std::ostream& out) const | ||||||
|  | { | ||||||
|  |     func_decl_set bound_decls, aux_decls; | ||||||
|  |     collect_decls_proc collect_decls(bound_decls, aux_decls); | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0; i < m_relation_info.size(); ++i) { | ||||||
|  |         bound_decls.insert(m_relation_info[i].m_pred); | ||||||
|  |         func_decl_ref_vector const& sig = m_relation_info[i].m_vars; | ||||||
|  |         for (unsigned j = 0; j < sig.size(); ++j) { | ||||||
|  |             bound_decls.insert(sig[j]); | ||||||
|  |         } | ||||||
|  |         for_each_expr(collect_decls, m_relation_info[i].m_body); | ||||||
|  |     } | ||||||
|  |     for (unsigned i = 0; i < rules.size(); ++i) { | ||||||
|  |         bound_decls.insert(rules[i]->get_decl()); | ||||||
|  |     } | ||||||
|  |     for (unsigned i = 0; i < rules.size(); ++i) { | ||||||
|  |         unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); | ||||||
|  |         unsigned t_sz = rules[i]->get_tail_size(); | ||||||
|  |         for (unsigned j = u_sz; j < t_sz; ++j) { | ||||||
|  |             for_each_expr(collect_decls, rules[i]->get_tail(j)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     smt2_pp_environment_dbg env(m); | ||||||
|  |     func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end(); | ||||||
|  |     for (; it != end; ++it) { | ||||||
|  |         func_decl* f = *it; | ||||||
|  |         ast_smt2_pp(out, f, env); | ||||||
|  |         out << "\n"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     out << to_string() << "\n"; | ||||||
|  |     for (unsigned i = 0; i < rules.size(); ++i) { | ||||||
|  |         out << "(push)\n"; | ||||||
|  |         out << "(assert (not\n"; | ||||||
|  |         rm.display_smt2(*rules[i], out); | ||||||
|  |         out << "))\n"; | ||||||
|  |         out << "(check-sat)\n"; | ||||||
|  |         out << "(pop)\n"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<std::string> manager::get_state_suffixes() | ||||||
|  | { | ||||||
|  |     std::vector<std::string> res; | ||||||
|  |     res.push_back("_n"); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | manager::manager(unsigned max_num_contexts, ast_manager& manager) : | ||||||
|  |     m(manager), | ||||||
|  |     m_brwr(m), | ||||||
|  |     m_mux(m, get_state_suffixes()), | ||||||
|  |     m_background(m.mk_true(), m), | ||||||
|  |     m_contexts(m, max_num_contexts), | ||||||
|  |     m_contexts2(m, max_num_contexts), | ||||||
|  |     m_contexts3(m, max_num_contexts), | ||||||
|  |     m_next_unique_num(0) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void manager::add_new_state(func_decl * s) | ||||||
|  | { | ||||||
|  |     SASSERT(s->get_arity() == 0); //we currently don't support non-constant states
 | ||||||
|  |     decl_vector vect; | ||||||
|  | 
 | ||||||
|  |     SASSERT(o_index(0) == 1); //we assume this in the number of retrieved symbols
 | ||||||
|  |     m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); | ||||||
|  |     m_o0_preds.push_back(vect[o_index(0)]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func_decl * manager::get_o_pred(func_decl* s, unsigned idx) | ||||||
|  | { | ||||||
|  |     func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx)); | ||||||
|  |     if (res) { return res; } | ||||||
|  |     add_new_state(s); | ||||||
|  |     res = m_mux.try_get_by_prefix(s, o_index(idx)); | ||||||
|  |     SASSERT(res); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func_decl * manager::get_n_pred(func_decl* s) | ||||||
|  | { | ||||||
|  |     func_decl * res = m_mux.try_get_by_prefix(s, n_index()); | ||||||
|  |     if (res) { return res; } | ||||||
|  |     add_new_state(s); | ||||||
|  |     res = m_mux.try_get_by_prefix(s, n_index()); | ||||||
|  |     SASSERT(res); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) | ||||||
|  | { | ||||||
|  |     m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) | ||||||
|  | { | ||||||
|  |     m_brwr.mk_and(core.size(), core.c_ptr(), res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) | ||||||
|  | { | ||||||
|  |     m_brwr.mk_not(cube, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) | ||||||
|  | { | ||||||
|  |     m_brwr.mk_not(lemma, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref manager::mk_and(unsigned sz, expr* const* exprs) | ||||||
|  | { | ||||||
|  |     expr_ref result(m); | ||||||
|  |     m_brwr.mk_and(sz, exprs, result); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref manager::mk_or(unsigned sz, expr* const* exprs) | ||||||
|  | { | ||||||
|  |     expr_ref result(m); | ||||||
|  |     m_brwr.mk_or(sz, exprs, result); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref manager::mk_not_and(expr_ref_vector const& conjs) | ||||||
|  | { | ||||||
|  |     expr_ref result(m), e(m); | ||||||
|  |     expr_ref_vector es(conjs); | ||||||
|  |     flatten_and(es); | ||||||
|  |     for (unsigned i = 0; i < es.size(); ++i) { | ||||||
|  |         m_brwr.mk_not(es[i].get(), e); | ||||||
|  |         es[i] = e; | ||||||
|  |     } | ||||||
|  |     m_brwr.mk_or(es.size(), es.c_ptr(), result); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void manager::get_or(expr* e, expr_ref_vector& result) | ||||||
|  | { | ||||||
|  |     result.push_back(e); | ||||||
|  |     for (unsigned i = 0; i < result.size();) { | ||||||
|  |         e = result[i].get(); | ||||||
|  |         if (m.is_or(e)) { | ||||||
|  |             result.append(to_app(e)->get_num_args(), to_app(e)->get_args()); | ||||||
|  |             result[i] = result.back(); | ||||||
|  |             result.pop_back(); | ||||||
|  |         } else { | ||||||
|  |             ++i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value) | ||||||
|  | { | ||||||
|  |     if (!is_app(atom0)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     app * atom = to_app(atom0); | ||||||
|  |     expr * arg1; | ||||||
|  |     expr * arg2; | ||||||
|  |     app * candidate_state; | ||||||
|  |     app_ref candidate_value(m); | ||||||
|  |     if (m.is_not(atom, arg1)) { | ||||||
|  |         if (!is_app(arg1)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         candidate_state = to_app(arg1); | ||||||
|  |         candidate_value = m.mk_false(); | ||||||
|  |     } else if (m.is_eq(atom, arg1, arg2)) { | ||||||
|  |         if (!is_app(arg1) || !is_app(arg2)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (!m_mux.is_muxed(to_app(arg1)->get_decl())) { | ||||||
|  |             std::swap(arg1, arg2); | ||||||
|  |         } | ||||||
|  |         candidate_state = to_app(arg1); | ||||||
|  |         candidate_value = to_app(arg2); | ||||||
|  |     } else { | ||||||
|  |         candidate_state = atom; | ||||||
|  |         candidate_value = m.mk_true(); | ||||||
|  |     } | ||||||
|  |     if (!m_mux.is_muxed(candidate_state->get_decl())) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     state = candidate_state; | ||||||
|  |     value = candidate_value; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) | ||||||
|  | { | ||||||
|  |     app_ref dummy_value_holder(m); | ||||||
|  |     app * s; | ||||||
|  |     if (try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) { | ||||||
|  |         state = s->get_decl(); | ||||||
|  |         return true; | ||||||
|  |     } else { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Create a new skolem constant | ||||||
|  |  */ | ||||||
|  | app* mk_zk_const(ast_manager &m, unsigned idx, sort *s) { | ||||||
|  |     std::stringstream name; | ||||||
|  |     name << "sk!" << idx; | ||||||
|  |     return m.mk_const(symbol(name.str().c_str()), s); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace find_zk_const_ns { | ||||||
|  | struct proc { | ||||||
|  |     app_ref_vector &m_out; | ||||||
|  |     proc (app_ref_vector &out) : m_out(out) {} | ||||||
|  |     void operator() (var const * n) const {} | ||||||
|  |     void operator() (app *n) const { | ||||||
|  |         if (is_uninterp_const(n) && | ||||||
|  |             n->get_decl()->get_name().str().compare (0, 3, "sk!") == 0) { | ||||||
|  |             m_out.push_back (n); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator() (quantifier const *n) const {} | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void find_zk_const(expr *e, app_ref_vector &res) { | ||||||
|  |     find_zk_const_ns::proc p(res); | ||||||
|  |     for_each_expr (p, e); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace has_zk_const_ns { | ||||||
|  | struct found {}; | ||||||
|  | struct proc { | ||||||
|  |     void operator() (var const *n) const {} | ||||||
|  |     void operator() (app const *n) const { | ||||||
|  |         if (is_uninterp_const(n) && | ||||||
|  |             n->get_decl()->get_name().str().compare(0, 3, "sk!") == 0) { | ||||||
|  |             throw found(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator() (quantifier const *n) const {} | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool has_zk_const(expr *e){ | ||||||
|  |     has_zk_const_ns::proc p; | ||||||
|  |     try { | ||||||
|  |         for_each_expr(p, e); | ||||||
|  |     } | ||||||
|  |     catch (has_zk_const_ns::found) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										345
									
								
								src/muz/spacer/spacer_manager.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								src/muz/spacer/spacer_manager.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,345 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_manager.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     A manager class for SPACER, taking care of creating of AST | ||||||
|  |     objects and conversions between them. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-8-25. | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_MANAGER_H_ | ||||||
|  | #define _SPACER_MANAGER_H_ | ||||||
|  | 
 | ||||||
|  | #include <utility> | ||||||
|  | #include <map> | ||||||
|  | #include "bool_rewriter.h" | ||||||
|  | #include "expr_replacer.h" | ||||||
|  | #include "expr_substitution.h" | ||||||
|  | #include "map.h" | ||||||
|  | #include "ref_vector.h" | ||||||
|  | #include "smt_kernel.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "spacer_sym_mux.h" | ||||||
|  | #include "spacer_farkas_learner.h" | ||||||
|  | #include "spacer_smt_context_manager.h" | ||||||
|  | #include "dl_rule.h" | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | namespace smt { | ||||||
|  | class context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | struct relation_info { | ||||||
|  |     func_decl_ref         m_pred; | ||||||
|  |     func_decl_ref_vector  m_vars; | ||||||
|  |     expr_ref              m_body; | ||||||
|  |     relation_info(ast_manager& m, func_decl* pred, ptr_vector<func_decl> const& vars, expr* b): | ||||||
|  |         m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {} | ||||||
|  |     relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class unknown_exception {}; | ||||||
|  | 
 | ||||||
|  | class inductive_property { | ||||||
|  |     ast_manager&             m; | ||||||
|  |     model_converter_ref      m_mc; | ||||||
|  |     vector<relation_info>    m_relation_info; | ||||||
|  |     expr_ref fixup_clauses(expr* property) const; | ||||||
|  |     expr_ref fixup_clause(expr* clause) const; | ||||||
|  | public: | ||||||
|  |     inductive_property(ast_manager& m, model_converter_ref& mc, vector<relation_info> const& relations): | ||||||
|  |         m(m), | ||||||
|  |         m_mc(mc), | ||||||
|  |         m_relation_info(relations) {} | ||||||
|  | 
 | ||||||
|  |     std::string to_string() const; | ||||||
|  | 
 | ||||||
|  |     expr_ref to_expr() const; | ||||||
|  | 
 | ||||||
|  |     void to_model(model_ref& md) const; | ||||||
|  | 
 | ||||||
|  |     void display(datalog::rule_manager& rm, ptr_vector<datalog::rule> const& rules, std::ostream& out) const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class manager { | ||||||
|  |     ast_manager&      m; | ||||||
|  | 
 | ||||||
|  |     mutable bool_rewriter m_brwr; | ||||||
|  | 
 | ||||||
|  |     sym_mux               m_mux; | ||||||
|  |     expr_ref              m_background; | ||||||
|  |     decl_vector           m_o0_preds; | ||||||
|  |     spacer::smt_context_manager   m_contexts; | ||||||
|  |     spacer::smt_context_manager   m_contexts2; | ||||||
|  |     spacer::smt_context_manager   m_contexts3; | ||||||
|  | 
 | ||||||
|  |     /** whenever we need an unique number, we get this one and increase */ | ||||||
|  |     unsigned m_next_unique_num; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     static std::vector<std::string> get_state_suffixes(); | ||||||
|  | 
 | ||||||
|  |     unsigned n_index() const { return 0; } | ||||||
|  |     unsigned o_index(unsigned i) const { return i + 1; } | ||||||
|  | 
 | ||||||
|  |     void add_new_state(func_decl * s); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     manager(unsigned max_num_contexts, ast_manager & manager); | ||||||
|  | 
 | ||||||
|  |     ast_manager& get_manager() const { return m; } | ||||||
|  |     bool_rewriter& get_brwr() const { return m_brwr; } | ||||||
|  | 
 | ||||||
|  |     expr_ref mk_and(unsigned sz, expr* const* exprs); | ||||||
|  |     expr_ref mk_and(expr_ref_vector const& exprs) | ||||||
|  |     { | ||||||
|  |         return mk_and(exprs.size(), exprs.c_ptr()); | ||||||
|  |     } | ||||||
|  |     expr_ref mk_and(expr* a, expr* b) | ||||||
|  |     { | ||||||
|  |         expr* args[2] = { a, b }; | ||||||
|  |         return mk_and(2, args); | ||||||
|  |     } | ||||||
|  |     expr_ref mk_or(unsigned sz, expr* const* exprs); | ||||||
|  |     expr_ref mk_or(expr_ref_vector const& exprs) | ||||||
|  |     { | ||||||
|  |         return mk_or(exprs.size(), exprs.c_ptr()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     expr_ref mk_not_and(expr_ref_vector const& exprs); | ||||||
|  | 
 | ||||||
|  |     void get_or(expr* e, expr_ref_vector& result); | ||||||
|  | 
 | ||||||
|  |     //"o" predicates stand for the old states and "n" for the new states
 | ||||||
|  |     func_decl * get_o_pred(func_decl * s, unsigned idx); | ||||||
|  |     func_decl * get_n_pred(func_decl * s); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Marks symbol as non-model which means it will not appear in models collected by | ||||||
|  |        get_state_cube_from_model function. | ||||||
|  |        This is to take care of auxiliary symbols introduced by the disjunction relations | ||||||
|  |        to relativize lemmas coming from disjuncts. | ||||||
|  |     */ | ||||||
|  |     void mark_as_non_model(func_decl * p) | ||||||
|  |     { | ||||||
|  |         m_mux.mark_as_non_model(p); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); } | ||||||
|  |     func_decl * const * end_o0_preds() const { return m_o0_preds.end(); } | ||||||
|  | 
 | ||||||
|  |     bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); } | ||||||
|  |     func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); } | ||||||
|  | 
 | ||||||
|  |     bool is_o(func_decl * p, unsigned idx) const | ||||||
|  |     { | ||||||
|  |         return m_mux.has_index(p, o_index(idx)); | ||||||
|  |     } | ||||||
|  |     void get_o_index(func_decl* p, unsigned& idx) const | ||||||
|  |     { | ||||||
|  |         m_mux.try_get_index(p, idx); | ||||||
|  |         SASSERT(idx != n_index()); | ||||||
|  |         idx--;  // m_mux has indices starting at 1
 | ||||||
|  |     } | ||||||
|  |     bool is_o(expr* e, unsigned idx) const | ||||||
|  |     { | ||||||
|  |         return is_app(e) && is_o(to_app(e)->get_decl(), idx); | ||||||
|  |     } | ||||||
|  |     bool is_o(func_decl * p) const | ||||||
|  |     { | ||||||
|  |         unsigned idx; | ||||||
|  |         return m_mux.try_get_index(p, idx) && idx != n_index(); | ||||||
|  |     } | ||||||
|  |     bool is_o(expr* e) const | ||||||
|  |     { | ||||||
|  |         return is_app(e) && is_o(to_app(e)->get_decl()); | ||||||
|  |     } | ||||||
|  |     bool is_n(func_decl * p) const | ||||||
|  |     { | ||||||
|  |         return m_mux.has_index(p, n_index()); | ||||||
|  |     } | ||||||
|  |     bool is_n(expr* e) const | ||||||
|  |     { | ||||||
|  |         return is_app(e) && is_n(to_app(e)->get_decl()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** true if p should not appead in models propagates into child relations */ | ||||||
|  |     bool is_non_model_sym(func_decl * p) const | ||||||
|  |     { return m_mux.is_non_model_sym(p); } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /** true if f doesn't contain any n predicates */ | ||||||
|  |     bool is_o_formula(expr * f) const | ||||||
|  |     { | ||||||
|  |         return !m_mux.contains(f, n_index()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** true if f contains only o state preds of index o_idx */ | ||||||
|  |     bool is_o_formula(expr * f, unsigned o_idx) const | ||||||
|  |     { | ||||||
|  |         return m_mux.is_homogenous_formula(f, o_index(o_idx)); | ||||||
|  |     } | ||||||
|  |     /** true if f doesn't contain any o predicates */ | ||||||
|  |     bool is_n_formula(expr * f) const | ||||||
|  |     { | ||||||
|  |         return m_mux.is_homogenous_formula(f, n_index()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     func_decl * o2n(func_decl * p, unsigned o_idx) const | ||||||
|  |     { | ||||||
|  |         return m_mux.conv(p, o_index(o_idx), n_index()); | ||||||
|  |     } | ||||||
|  |     func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const | ||||||
|  |     { | ||||||
|  |         return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx)); | ||||||
|  |     } | ||||||
|  |     func_decl * n2o(func_decl * p, unsigned o_idx) const | ||||||
|  |     { | ||||||
|  |         return m_mux.conv(p, n_index(), o_index(o_idx)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const | ||||||
|  |     { m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); } | ||||||
|  | 
 | ||||||
|  |     void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const | ||||||
|  |     { m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); } | ||||||
|  | 
 | ||||||
|  |     void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const | ||||||
|  |     { m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); } | ||||||
|  | 
 | ||||||
|  |     void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const | ||||||
|  |     { m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Return true if all state symbols which e contains are of one kind (either "n" or one of "o"). | ||||||
|  |     */ | ||||||
|  |     bool is_homogenous_formula(expr * e) const | ||||||
|  |     { | ||||||
|  |         return m_mux.is_homogenous_formula(e); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         Collect indices used in expression. | ||||||
|  |     */ | ||||||
|  |     void collect_indices(expr* e, unsigned_vector& indices) const | ||||||
|  |     { | ||||||
|  |         m_mux.collect_indices(e, indices); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         Collect used variables of each index. | ||||||
|  |     */ | ||||||
|  |     void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const | ||||||
|  |     { | ||||||
|  |         m_mux.collect_variables(e, vars); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Return true iff both s1 and s2 are either "n" or "o" of the same index. | ||||||
|  |        If one (or both) of them are not state symbol, return false. | ||||||
|  |     */ | ||||||
|  |     bool have_different_state_kinds(func_decl * s1, func_decl * s2) const | ||||||
|  |     { | ||||||
|  |         unsigned i1, i2; | ||||||
|  |         return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1 != i2; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Increase indexes of state symbols in formula by dist. | ||||||
|  |        The 'N' index becomes 'O' index with number dist-1. | ||||||
|  |     */ | ||||||
|  |     void formula_shift(expr * src, expr_ref & tgt, unsigned dist) const | ||||||
|  |     { | ||||||
|  |         SASSERT(n_index() == 0); | ||||||
|  |         SASSERT(o_index(0) == 1); | ||||||
|  |         m_mux.shift_formula(src, dist, tgt); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res); | ||||||
|  |     void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res); | ||||||
|  |     void mk_cube_into_lemma(expr * cube, expr_ref & res); | ||||||
|  |     void mk_lemma_into_cube(expr * lemma, expr_ref & res); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Remove from vec all atoms that do not have an "o" state. | ||||||
|  |        The order of elements in vec may change. | ||||||
|  |        An assumption is that atoms having "o" state of given index | ||||||
|  |        do not have "o" states of other indexes or "n" states. | ||||||
|  |     */ | ||||||
|  |     void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const | ||||||
|  |     { m_mux.filter_idx(vec, o_index(o_idx)); } | ||||||
|  |     void filter_n_atoms(expr_ref_vector& vec) const | ||||||
|  |     { m_mux.filter_idx(vec, n_index()); } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Partition literals into o_lits and others. | ||||||
|  |     */ | ||||||
|  |     void partition_o_atoms(expr_ref_vector const& lits, | ||||||
|  |                            expr_ref_vector& o_lits, | ||||||
|  |                            expr_ref_vector& other, | ||||||
|  |                            unsigned o_idx) const | ||||||
|  |     { | ||||||
|  |         m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void filter_out_non_model_atoms(expr_ref_vector& vec) const | ||||||
|  |     { m_mux.filter_non_model_lits(vec); } | ||||||
|  | 
 | ||||||
|  |     bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); | ||||||
|  |     bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     std::string pp_model(const model_core & mdl) const | ||||||
|  |     {  return m_mux.pp_model(mdl); } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void set_background(expr* b) { m_background = b; } | ||||||
|  | 
 | ||||||
|  |     expr* get_background() const { return m_background; } | ||||||
|  | 
 | ||||||
|  |     unsigned get_unique_num() { return m_next_unique_num++; } | ||||||
|  | 
 | ||||||
|  |     solver* mk_fresh() {return m_contexts.mk_fresh();} | ||||||
|  |     smt_params& fparams() { return m_contexts.fparams(); } | ||||||
|  |     solver* mk_fresh2() {return m_contexts2.mk_fresh();} | ||||||
|  |     smt_params &fparams2() { return m_contexts2.fparams(); } | ||||||
|  |     solver* mk_fresh3() {return m_contexts3.mk_fresh();} | ||||||
|  |     smt_params &fparams3() {return m_contexts3.fparams();} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const | ||||||
|  |     { | ||||||
|  |         m_contexts.collect_statistics(st); | ||||||
|  |         m_contexts2.collect_statistics(st); | ||||||
|  |         m_contexts3.collect_statistics(st); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void reset_statistics() | ||||||
|  |     { | ||||||
|  |         m_contexts.reset_statistics(); | ||||||
|  |         m_contexts2.reset_statistics(); | ||||||
|  |         m_contexts3.reset_statistics(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | app* mk_zk_const (ast_manager &m, unsigned idx, sort *s); | ||||||
|  | void find_zk_const(expr* e, app_ref_vector &out); | ||||||
|  | bool has_zk_const(expr* e); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										55
									
								
								src/muz/spacer/spacer_marshal.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/muz/spacer/spacer_marshal.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |    spacer_marshal.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    marshaling and unmarshaling of expressions | ||||||
|  | 
 | ||||||
|  |    --*/ | ||||||
|  | #include "spacer_marshal.h" | ||||||
|  | 
 | ||||||
|  | #include <sstream> | ||||||
|  | #include "cmd_context.h" | ||||||
|  | #include "smt2parser.h" | ||||||
|  | #include "vector.h" | ||||||
|  | #include "ast_smt_pp.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m) | ||||||
|  | { | ||||||
|  |     ast_smt_pp pp(m); | ||||||
|  |     pp.display_smt2(os, e); | ||||||
|  |     return os; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string marshal(expr_ref e, ast_manager &m) | ||||||
|  | { | ||||||
|  |     std::stringstream ss; | ||||||
|  |     marshal(ss, e, m); | ||||||
|  |     return ss.str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | expr_ref unmarshal(std::istream &is, ast_manager &m) | ||||||
|  | { | ||||||
|  |     cmd_context ctx(false, &m); | ||||||
|  |     ctx.set_ignore_check(true); | ||||||
|  |     if (!parse_smt2_commands(ctx, is)) { return expr_ref(0, m); } | ||||||
|  | 
 | ||||||
|  |     ptr_vector<expr>::const_iterator it  = ctx.begin_assertions(); | ||||||
|  |     ptr_vector<expr>::const_iterator end = ctx.end_assertions(); | ||||||
|  |     if (it == end) { return expr_ref(m.mk_true(), m); } | ||||||
|  |     unsigned size = static_cast<unsigned>(end - it); | ||||||
|  |     return expr_ref(m.mk_and(size, it), m); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | expr_ref unmarshal(std::string s, ast_manager &m) | ||||||
|  | { | ||||||
|  |     std::istringstream is(s); | ||||||
|  |     return unmarshal(is, m); | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								src/muz/spacer/spacer_marshal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/muz/spacer/spacer_marshal.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |    spacer_marshal.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    marshaling and unmarshaling of expressions | ||||||
|  | 
 | ||||||
|  |    --*/ | ||||||
|  | #ifndef _SPACER_MARSHAL_H_ | ||||||
|  | #define _SPACER_MARSHAL_H_ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include "ast.h" | ||||||
|  | #include <iostream> | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m); | ||||||
|  | std::string marshal(expr_ref e, ast_manager &m); | ||||||
|  | expr_ref unmarshal(std::string s, ast_manager &m); | ||||||
|  | expr_ref unmarshal(std::istream &is, ast_manager &m); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										159
									
								
								src/muz/spacer/spacer_matrix.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/muz/spacer/spacer_matrix.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,159 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_matrix.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |     a matrix | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include "spacer_matrix.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer | ||||||
|  | { | ||||||
|  |     spacer_matrix::spacer_matrix(unsigned m, unsigned n) : m_num_rows(m), m_num_cols(n) | ||||||
|  |     { | ||||||
|  |         for (unsigned i=0; i < m; ++i) | ||||||
|  |         { | ||||||
|  |             vector<rational> v; | ||||||
|  |             for (unsigned j=0; j < n; ++j) | ||||||
|  |             { | ||||||
|  |                 v.push_back(rational(0)); | ||||||
|  |             } | ||||||
|  |             m_matrix.push_back(v); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned spacer_matrix::num_rows() | ||||||
|  |     { | ||||||
|  |         return m_num_rows; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned spacer_matrix::num_cols() | ||||||
|  |     { | ||||||
|  |         return m_num_cols; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     rational spacer_matrix::get(unsigned int i, unsigned int j) | ||||||
|  |     { | ||||||
|  |         SASSERT(i < m_num_rows); | ||||||
|  |         SASSERT(j < m_num_cols); | ||||||
|  | 
 | ||||||
|  |         return m_matrix[i][j]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_matrix::set(unsigned int i, unsigned int j, rational v) | ||||||
|  |     { | ||||||
|  |         SASSERT(i < m_num_rows); | ||||||
|  |         SASSERT(j < m_num_cols); | ||||||
|  | 
 | ||||||
|  |         m_matrix[i][j] = v; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned spacer_matrix::perform_gaussian_elimination() | ||||||
|  |     { | ||||||
|  |         unsigned i=0; | ||||||
|  |         unsigned j=0; | ||||||
|  |         while(i < m_matrix.size() && j < m_matrix[0].size()) | ||||||
|  |         { | ||||||
|  |             // find maximal element in column with row index bigger or equal i
 | ||||||
|  |             rational max = m_matrix[i][j]; | ||||||
|  |             unsigned max_index = i; | ||||||
|  | 
 | ||||||
|  |             for (unsigned k=i+1; k < m_matrix.size(); ++k) | ||||||
|  |             { | ||||||
|  |                 if (max < m_matrix[k][j]) | ||||||
|  |                 { | ||||||
|  |                     max = m_matrix[k][j]; | ||||||
|  |                     max_index = k; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (max.is_zero()) // skip this column
 | ||||||
|  |             { | ||||||
|  |                 ++j; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 // reorder rows if necessary
 | ||||||
|  |                 vector<rational> tmp = m_matrix[i]; | ||||||
|  |                 m_matrix[i] = m_matrix[max_index]; | ||||||
|  |                 m_matrix[max_index] = m_matrix[i]; | ||||||
|  | 
 | ||||||
|  |                 // normalize row
 | ||||||
|  |                 rational pivot = m_matrix[i][j]; | ||||||
|  |                 if (!pivot.is_one()) | ||||||
|  |                 { | ||||||
|  |                     for (unsigned k=0; k < m_matrix[i].size(); ++k) | ||||||
|  |                     { | ||||||
|  |                         m_matrix[i][k] = m_matrix[i][k] / pivot; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // subtract row from all other rows
 | ||||||
|  |                 for (unsigned k=1; k < m_matrix.size(); ++k) | ||||||
|  |                 { | ||||||
|  |                     if (k != i) | ||||||
|  |                     { | ||||||
|  |                         rational factor = m_matrix[k][j]; | ||||||
|  |                         for (unsigned l=0; l < m_matrix[k].size(); ++l) | ||||||
|  |                         { | ||||||
|  |                             m_matrix[k][l] = m_matrix[k][l] - (factor * m_matrix[i][l]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 ++i; | ||||||
|  |                 ++j; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (get_verbosity_level() >= 1) | ||||||
|  |         { | ||||||
|  |             SASSERT(m_matrix.size() > 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return i; //i points to the row after the last row which is non-zero
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_matrix::print_matrix() | ||||||
|  |     { | ||||||
|  |         verbose_stream() << "\nMatrix\n"; | ||||||
|  |         for (const auto& row : m_matrix) | ||||||
|  |         { | ||||||
|  |             for (const auto& element : row) | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << element << ", "; | ||||||
|  |             } | ||||||
|  |             verbose_stream() << "\n"; | ||||||
|  |         } | ||||||
|  |         verbose_stream() << "\n"; | ||||||
|  |     } | ||||||
|  |     void spacer_matrix::normalize() | ||||||
|  |     { | ||||||
|  |         rational den = rational::one(); | ||||||
|  |         for (unsigned i=0; i < m_num_rows; ++i) | ||||||
|  |         { | ||||||
|  |             for (unsigned j=0; j < m_num_cols; ++j) | ||||||
|  |             { | ||||||
|  |                 den = lcm(den, denominator(m_matrix[i][j])); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for (unsigned i=0; i < m_num_rows; ++i) | ||||||
|  |         { | ||||||
|  |             for (unsigned j=0; j < m_num_cols; ++j) | ||||||
|  |             { | ||||||
|  |                 m_matrix[i][j] = den * m_matrix[i][j]; | ||||||
|  |                 SASSERT(m_matrix[i][j].is_int()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								src/muz/spacer/spacer_matrix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/muz/spacer/spacer_matrix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_matrix.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |     a matrix | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef _SPACER_MATRIX_H_ | ||||||
|  | #define _SPACER_MATRIX_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  |     class spacer_matrix { | ||||||
|  |     public: | ||||||
|  |         spacer_matrix(unsigned m, unsigned n); // m rows, n colums
 | ||||||
|  | 
 | ||||||
|  |         unsigned num_rows(); | ||||||
|  |         unsigned num_cols(); | ||||||
|  | 
 | ||||||
|  |         rational get(unsigned i, unsigned j); | ||||||
|  |         void set(unsigned i, unsigned j, rational v); | ||||||
|  | 
 | ||||||
|  |         unsigned perform_gaussian_elimination(); | ||||||
|  | 
 | ||||||
|  |         void print_matrix(); | ||||||
|  |         void normalize(); | ||||||
|  |     private: | ||||||
|  |         unsigned m_num_rows; | ||||||
|  |         unsigned m_num_cols; | ||||||
|  |         vector<vector<rational>> m_matrix; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										217
									
								
								src/muz/spacer/spacer_mev_array.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/muz/spacer/spacer_mev_array.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,217 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     model_mev_array.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Evaluate array expressions in a given model. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include"model.h" | ||||||
|  | #include"model_evaluator_params.hpp" | ||||||
|  | #include"rewriter_types.h" | ||||||
|  | #include"model_evaluator.h" | ||||||
|  | #include"spacer_mev_array.h" | ||||||
|  | #include"bool_rewriter.h" | ||||||
|  | #include"arith_rewriter.h" | ||||||
|  | #include"bv_rewriter.h" | ||||||
|  | #include"datatype_rewriter.h" | ||||||
|  | #include"array_rewriter.h" | ||||||
|  | #include"rewriter_def.h" | ||||||
|  | #include"cooperate.h" | ||||||
|  | #include"ast_pp.h" | ||||||
|  | #include"func_interp.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // model_evaluator_array_util
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void model_evaluator_array_util::eval_exprs(model& mdl, expr_ref_vector& es) { | ||||||
|  |     for (unsigned j = 0; j < es.size(); ++j) { | ||||||
|  |         if (m_array.is_as_array(es.get (j))) { | ||||||
|  |             expr_ref r (m); | ||||||
|  |             eval(mdl, es.get (j), r); | ||||||
|  |             es.set (j, r); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool model_evaluator_array_util::extract_array_func_interp(model& mdl, expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case) { | ||||||
|  |     SASSERT(m_array.is_array(a)); | ||||||
|  | 
 | ||||||
|  |     TRACE("model_evaluator", tout << mk_pp(a, m) << "\n";); | ||||||
|  |     while (m_array.is_store(a)) { | ||||||
|  |         expr_ref_vector store(m); | ||||||
|  |         store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); | ||||||
|  |         eval_exprs(mdl, store); | ||||||
|  |         stores.push_back(store); | ||||||
|  |         a = to_app(a)->get_arg(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m_array.is_const(a)) { | ||||||
|  |         else_case = to_app(a)->get_arg(0); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while (m_array.is_as_array(a)) { | ||||||
|  |         func_decl* f = m_array.get_as_array_func_decl(to_app(a)); | ||||||
|  |         func_interp* g = mdl.get_func_interp(f); | ||||||
|  |         unsigned sz = g->num_entries(); | ||||||
|  |         unsigned arity = f->get_arity(); | ||||||
|  |         for (unsigned i = 0; i < sz; ++i) { | ||||||
|  |             expr_ref_vector store(m); | ||||||
|  |             func_entry const* fe = g->get_entry(i); | ||||||
|  |             store.append(arity, fe->get_args()); | ||||||
|  |             store.push_back(fe->get_result()); | ||||||
|  |             for (unsigned j = 0; j < store.size(); ++j) { | ||||||
|  |                 if (!is_ground(store[j].get())) { | ||||||
|  |                     TRACE("model_evaluator", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             eval_exprs(mdl, store); | ||||||
|  |             stores.push_back(store); | ||||||
|  |         } | ||||||
|  |         else_case = g->get_else(); | ||||||
|  |         if (!else_case) { | ||||||
|  |             TRACE("model_evaluator", tout << "no else case " << mk_pp(a, m) << "\n";); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (!is_ground(else_case)) { | ||||||
|  |             TRACE("model_evaluator", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (m_array.is_as_array(else_case)) { | ||||||
|  |             expr_ref r (m); | ||||||
|  |             eval(mdl, else_case, r); | ||||||
|  |             else_case = r; | ||||||
|  |         } | ||||||
|  |         TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m) << "\n";); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     TRACE("model_evaluator", tout << "no translation: " << mk_pp(a, m) << "\n";); | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator_array_util::eval_array_eq(model& mdl, app* e, expr* arg1, expr* arg2, expr_ref& res) { | ||||||
|  |     TRACE("model_evaluator", tout << "array equality: " << mk_pp(e, m) << "\n";); | ||||||
|  |     expr_ref v1(m), v2(m); | ||||||
|  |     eval (mdl, arg1, v1); | ||||||
|  |     eval (mdl, arg2, v2); | ||||||
|  |     if (v1 == v2) { | ||||||
|  |         res = m.mk_true (); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     sort* s = m.get_sort(arg1); | ||||||
|  |     sort* r = get_array_range(s); | ||||||
|  |     // give up evaluating finite domain/range arrays
 | ||||||
|  |     if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { | ||||||
|  |         TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |         res.reset (); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     vector<expr_ref_vector> store; | ||||||
|  |     expr_ref else1(m), else2(m); | ||||||
|  |     if (!extract_array_func_interp(mdl, v1, store, else1) || | ||||||
|  |             !extract_array_func_interp(mdl, v2, store, else2)) { | ||||||
|  |         TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |         res.reset (); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (else1 != else2) { | ||||||
|  |         if (m.is_value(else1) && m.is_value(else2)) { | ||||||
|  |             TRACE("model_evaluator", tout | ||||||
|  |                     << "defaults are different: " << mk_pp(e, m) << " " | ||||||
|  |                     << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); | ||||||
|  |             res = m.mk_false (); | ||||||
|  |         } | ||||||
|  |         else if (m_array.is_array(else1)) { | ||||||
|  |             eval_array_eq(mdl, e, else1, else2, res); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |             res.reset (); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     expr_ref s1(m), s2(m), w1(m), w2(m); | ||||||
|  |     expr_ref_vector args1(m), args2(m); | ||||||
|  |     args1.push_back(v1); | ||||||
|  |     args2.push_back(v2); | ||||||
|  |     for (unsigned i = 0; i < store.size(); ++i) { | ||||||
|  |         args1.resize(1); | ||||||
|  |         args2.resize(1); | ||||||
|  |         args1.append(store[i].size()-1, store[i].c_ptr()); | ||||||
|  |         args2.append(store[i].size()-1, store[i].c_ptr()); | ||||||
|  |         s1 = m_array.mk_select(args1.size(), args1.c_ptr()); | ||||||
|  |         s2 = m_array.mk_select(args2.size(), args2.c_ptr()); | ||||||
|  |         eval (mdl, s1, w1); | ||||||
|  |         eval (mdl, s2, w2); | ||||||
|  |         if (w1 == w2) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (m.is_value(w1) && m.is_value(w2)) { | ||||||
|  |             TRACE("model_evaluator", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; | ||||||
|  |                     tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; | ||||||
|  |                     tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); | ||||||
|  |             res = m.mk_false (); | ||||||
|  |         } | ||||||
|  |         else if (m_array.is_array(w1)) { | ||||||
|  |             eval_array_eq(mdl, e, w1, w2, res); | ||||||
|  |             if (m.is_true (res)) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); | ||||||
|  |             res.reset (); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     res = m.mk_true (); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void model_evaluator_array_util::eval(model& mdl, expr* e, expr_ref& r, bool model_completion) { | ||||||
|  |     model_evaluator mev (mdl); | ||||||
|  |     mev.set_model_completion (model_completion); | ||||||
|  |     bool eval_result = true; | ||||||
|  |     try { | ||||||
|  |         mev (e, r); | ||||||
|  |     } | ||||||
|  |     catch (model_evaluator_exception &) { | ||||||
|  |         eval_result = false; | ||||||
|  |     } | ||||||
|  |     VERIFY(eval_result); | ||||||
|  | 
 | ||||||
|  |     if (m_array.is_array(e)) { | ||||||
|  |         vector<expr_ref_vector> stores; | ||||||
|  |         expr_ref_vector args(m); | ||||||
|  |         expr_ref else_case(m); | ||||||
|  |         if (extract_array_func_interp(mdl, r, stores, else_case)) { | ||||||
|  |             r = m_array.mk_const_array(m.get_sort(e), else_case); | ||||||
|  |             while (!stores.empty() && stores.back().back() == else_case) { | ||||||
|  |                 stores.pop_back(); | ||||||
|  |             } | ||||||
|  |             for (unsigned i = stores.size(); i > 0; ) { | ||||||
|  |                 --i; | ||||||
|  |                 args.resize(1); | ||||||
|  |                 args[0] = r; | ||||||
|  |                 args.append(stores[i]); | ||||||
|  |                 r = m_array.mk_store(args.size(), args.c_ptr()); | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/muz/spacer/spacer_mev_array.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/muz/spacer/spacer_mev_array.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_mev_array.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Utilities to evaluate arrays in the model. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |    based on model_evaluator in muz/pdr/pdr_util.h | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef _SPACER_MEV_ARRAY_H_ | ||||||
|  | #define _SPACER_MEV_ARRAY_H_ | ||||||
|  | 
 | ||||||
|  | #include"ast.h" | ||||||
|  | #include"rewriter_types.h" | ||||||
|  | #include"params.h" | ||||||
|  | #include "array_decl_plugin.h" | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * based on model_evaluator in muz/pdr/pdr_util.h | ||||||
|  |  */ | ||||||
|  | class model_evaluator_array_util { | ||||||
|  |     ast_manager&    m; | ||||||
|  |     array_util      m_array; | ||||||
|  | 
 | ||||||
|  |     void eval_exprs(model& mdl, expr_ref_vector& es); | ||||||
|  | 
 | ||||||
|  |     bool extract_array_func_interp(model& mdl, expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     model_evaluator_array_util (ast_manager& m): | ||||||
|  |         m (m), | ||||||
|  |         m_array (m) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * best effort evaluator of extensional array equality. | ||||||
|  |      */ | ||||||
|  |     void eval_array_eq(model& mdl, app* e, expr* arg1, expr* arg2, expr_ref& res); | ||||||
|  | 
 | ||||||
|  |     void eval(model& mdl, expr* e, expr_ref& r, bool model_completion = true); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										289
									
								
								src/muz/spacer/spacer_min_cut.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								src/muz/spacer/spacer_min_cut.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,289 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_min_cut.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |     min cut solver | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include "spacer_min_cut.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  |     spacer_min_cut::spacer_min_cut() | ||||||
|  |     { | ||||||
|  |         m_n = 2; | ||||||
|  | 
 | ||||||
|  |         // push back two empty vectors for source and sink
 | ||||||
|  |         m_edges.push_back(vector<std::pair<unsigned, unsigned>>()); | ||||||
|  |         m_edges.push_back(vector<std::pair<unsigned, unsigned>>()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned spacer_min_cut::new_node() | ||||||
|  |     { | ||||||
|  |         return m_n++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::add_edge(unsigned int i, unsigned int j, unsigned int capacity) | ||||||
|  |     { | ||||||
|  |         if (i >= m_edges.size()) | ||||||
|  |         { | ||||||
|  |             m_edges.resize(i + 1); | ||||||
|  |         } | ||||||
|  |         m_edges[i].insert(std::make_pair(j, 1)); | ||||||
|  |         STRACE("spacer.mincut", | ||||||
|  |                verbose_stream() << "adding edge (" << i << "," << j << ")\n"; | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::compute_min_cut(vector<unsigned>& cut_nodes) | ||||||
|  |     { | ||||||
|  |         if (m_n == 2) | ||||||
|  |         { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         m_d.resize(m_n); | ||||||
|  |         m_pred.resize(m_n); | ||||||
|  | 
 | ||||||
|  |         // compute initial distances and number of nodes
 | ||||||
|  |         compute_initial_distances(); | ||||||
|  | 
 | ||||||
|  |         unsigned i = 0; | ||||||
|  | 
 | ||||||
|  |         while (m_d[0] < m_n) | ||||||
|  |         { | ||||||
|  |             unsigned j = get_admissible_edge(i); | ||||||
|  | 
 | ||||||
|  |             if (j < m_n) | ||||||
|  |             { | ||||||
|  |                 // advance(i)
 | ||||||
|  |                 m_pred[j] = i; | ||||||
|  |                 i = j; | ||||||
|  | 
 | ||||||
|  |                 // if i is the sink, augment path
 | ||||||
|  |                 if (i == 1) | ||||||
|  |                 { | ||||||
|  |                     augment_path(); | ||||||
|  |                     i = 0; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 // retreat
 | ||||||
|  |                 compute_distance(i); | ||||||
|  |                 if (i != 0) | ||||||
|  |                 { | ||||||
|  |                     i = m_pred[i]; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // split nodes into reachable and unreachable ones
 | ||||||
|  |         vector<bool> reachable(m_n); | ||||||
|  |         compute_reachable_nodes(reachable); | ||||||
|  | 
 | ||||||
|  |         // find all edges between reachable and unreachable nodes and for each such edge, add corresponding lemma to unsat-core
 | ||||||
|  |         compute_cut_and_add_lemmas(reachable, cut_nodes); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::compute_initial_distances() | ||||||
|  |     { | ||||||
|  |         vector<unsigned> todo; | ||||||
|  |         vector<bool> visited(m_n); | ||||||
|  | 
 | ||||||
|  |         todo.push_back(0); // start at the source, since we do postorder traversel
 | ||||||
|  | 
 | ||||||
|  |         while (!todo.empty()) | ||||||
|  |         { | ||||||
|  |             unsigned current = todo.back(); | ||||||
|  | 
 | ||||||
|  |             // if we haven't already visited current
 | ||||||
|  |             if (!visited[current]) { | ||||||
|  |                 bool existsUnvisitedParent = false; | ||||||
|  | 
 | ||||||
|  |                 // add unprocessed parents to stack for DFS. If there is at least one unprocessed parent, don't compute the result
 | ||||||
|  |                 // for current now, but wait until those unprocessed parents are processed.
 | ||||||
|  |                 for (unsigned i = 0, sz = m_edges[current].size(); i < sz; ++i) | ||||||
|  |                 { | ||||||
|  |                     unsigned parent = m_edges[current][i].first; | ||||||
|  | 
 | ||||||
|  |                     // if we haven't visited the current parent yet
 | ||||||
|  |                     if(!visited[parent]) | ||||||
|  |                     { | ||||||
|  |                         // add it to the stack
 | ||||||
|  |                         todo.push_back(parent); | ||||||
|  |                         existsUnvisitedParent = true; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // if we already visited all parents, we can visit current too
 | ||||||
|  |                 if (!existsUnvisitedParent) { | ||||||
|  |                     visited[current] = true; | ||||||
|  |                     todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |                     compute_distance(current); // I.H. all parent distances are already computed
 | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 todo.pop_back(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned spacer_min_cut::get_admissible_edge(unsigned i) | ||||||
|  |     { | ||||||
|  |         for (const auto& pair : m_edges[i]) | ||||||
|  |         { | ||||||
|  |             if (pair.second > 0 && m_d[i] == m_d[pair.first] + 1) | ||||||
|  |             { | ||||||
|  |                 return pair.first; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return m_n; // no element found
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::augment_path() | ||||||
|  |     { | ||||||
|  |         // find bottleneck capacity
 | ||||||
|  |         unsigned max = std::numeric_limits<unsigned int>::max(); | ||||||
|  |         unsigned k = 1; | ||||||
|  |         while (k != 0) | ||||||
|  |         { | ||||||
|  |             unsigned l = m_pred[k]; | ||||||
|  |             for (const auto& pair : m_edges[l]) | ||||||
|  |             { | ||||||
|  |                 if (pair.first == k) | ||||||
|  |                 { | ||||||
|  |                     if (max > pair.second) | ||||||
|  |                     { | ||||||
|  |                         max = pair.second; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             k = l; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         k = 1; | ||||||
|  |         while (k != 0) | ||||||
|  |         { | ||||||
|  |             unsigned l = m_pred[k]; | ||||||
|  | 
 | ||||||
|  |             // decrease capacity
 | ||||||
|  |             for (auto& pair : m_edges[l]) | ||||||
|  |             { | ||||||
|  |                 if (pair.first == k) | ||||||
|  |                 { | ||||||
|  |                     pair.second -= max; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             // increase reverse flow
 | ||||||
|  |             bool already_exists = false; | ||||||
|  |             for (auto& pair : m_edges[k]) | ||||||
|  |             { | ||||||
|  |                 if (pair.first == l) | ||||||
|  |                 { | ||||||
|  |                     already_exists = true; | ||||||
|  |                     pair.second += max; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (!already_exists) | ||||||
|  |             { | ||||||
|  |                 m_edges[k].insert(std::make_pair(l, max)); | ||||||
|  |             } | ||||||
|  |             k = l; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::compute_distance(unsigned i) | ||||||
|  |     { | ||||||
|  |         if (i == 1) // sink node
 | ||||||
|  |         { | ||||||
|  |             m_d[1] = 0; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             unsigned min = std::numeric_limits<unsigned int>::max(); | ||||||
|  | 
 | ||||||
|  |             // find edge (i,j) with positive residual capacity and smallest distance
 | ||||||
|  |             for (const auto& pair : m_edges[i]) | ||||||
|  |             { | ||||||
|  |                 if (pair.second > 0) | ||||||
|  |                 { | ||||||
|  |                     unsigned tmp = m_d[pair.first] + 1; | ||||||
|  |                     if (tmp < min) | ||||||
|  |                     { | ||||||
|  |                         min = tmp; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             m_d[i] = min; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::compute_reachable_nodes(vector<bool>& reachable) | ||||||
|  |     { | ||||||
|  |         vector<unsigned> todo; | ||||||
|  | 
 | ||||||
|  |         todo.push_back(0); | ||||||
|  |         while (!todo.empty()) | ||||||
|  |         { | ||||||
|  |             unsigned current = todo.back(); | ||||||
|  |             todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |             if (!reachable[current]) | ||||||
|  |             { | ||||||
|  |                 reachable[current] = true; | ||||||
|  | 
 | ||||||
|  |                 for (const auto& pair : m_edges[current]) | ||||||
|  |                 { | ||||||
|  |                     if (pair.second > 0) | ||||||
|  |                     { | ||||||
|  |                         todo.push_back(pair.first); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void spacer_min_cut::compute_cut_and_add_lemmas(vector<bool>& reachable, vector<unsigned>& cut_nodes) | ||||||
|  |     { | ||||||
|  |         vector<unsigned> todo; | ||||||
|  |         vector<bool> visited(m_n); | ||||||
|  | 
 | ||||||
|  |         todo.push_back(0); | ||||||
|  |         while (!todo.empty()) | ||||||
|  |         { | ||||||
|  |             unsigned current = todo.back(); | ||||||
|  |             todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |             if (!visited[current]) | ||||||
|  |             { | ||||||
|  |                 visited[current] = true; | ||||||
|  | 
 | ||||||
|  |                 for (const auto& pair : m_edges[current]) | ||||||
|  |                 { | ||||||
|  |                     unsigned successor = pair.first; | ||||||
|  |                     if (reachable[successor]) | ||||||
|  |                     { | ||||||
|  |                         todo.push_back(successor); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         cut_nodes.push_back(successor); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										52
									
								
								src/muz/spacer/spacer_min_cut.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/muz/spacer/spacer_min_cut.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_min_cut.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |     min cut solver | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_MIN_CUT_H_ | ||||||
|  | #define _SPACER_MIN_CUT_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  |     class spacer_min_cut { | ||||||
|  |     public: | ||||||
|  |         spacer_min_cut(); | ||||||
|  | 
 | ||||||
|  |         unsigned new_node(); | ||||||
|  |         void add_edge(unsigned i, unsigned j, unsigned capacity); | ||||||
|  |         void compute_min_cut(vector<unsigned>& cut_nodes); | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  | 
 | ||||||
|  |         unsigned m_n; // number of vertices in the graph
 | ||||||
|  | 
 | ||||||
|  |         vector<vector<std::pair<unsigned, unsigned> > > m_edges; // map from node to all outgoing edges together with their weights (also contains "reverse edges")
 | ||||||
|  |         vector<unsigned> m_d; // approximation of distance from node to sink in residual graph
 | ||||||
|  |         vector<unsigned> m_pred; // predecessor-information for reconstruction of augmenting path
 | ||||||
|  |         vector<expr*> m_node_to_formula; // maps each node to the corresponding formula in the original proof
 | ||||||
|  | 
 | ||||||
|  |         void compute_initial_distances(); | ||||||
|  |         unsigned get_admissible_edge(unsigned i); | ||||||
|  |         void augment_path(); | ||||||
|  |         void compute_distance(unsigned i); | ||||||
|  |         void compute_reachable_nodes(vector<bool>& reachable); | ||||||
|  |         void compute_cut_and_add_lemmas(vector<bool>& reachable, vector<unsigned>& cut_nodes); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										231
									
								
								src/muz/spacer/spacer_notes.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/muz/spacer/spacer_notes.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,231 @@ | ||||||
|  | a queue contains a model_node | ||||||
|  | 
 | ||||||
|  | let n = leaves.pop_top () | ||||||
|  | 
 | ||||||
|  | if (!n.has_derivation ()) | ||||||
|  | 
 | ||||||
|  |    if n.pt ().must_reach (n.post ()) | ||||||
|  |      add parent of n to the leaves | ||||||
|  |      return | ||||||
|  | 
 | ||||||
|  |    check abstract reachability of n | ||||||
|  |     | ||||||
|  |    if must reachable then | ||||||
|  |       create new reachability fact for n.pt () | ||||||
|  |       add parent of n to the leaves | ||||||
|  |    else if may reachable then  | ||||||
|  |        create derivation d for n | ||||||
|  |        create model_node kid for the top of d | ||||||
|  |        add kid to the leaves | ||||||
|  | 
 | ||||||
|  |    else /* unreachable */ | ||||||
|  |        create a lemma for n.pt () | ||||||
|  |        p = parent of n | ||||||
|  |        p.reset_derivation() | ||||||
|  |        add p to the leaves | ||||||
|  |         | ||||||
|  | else if (n.has_derivation ()) | ||||||
|  |   | ||||||
|  |    create next model_node kid for n.get_derivation () | ||||||
|  |     | ||||||
|  |    if (kid != NULL)  | ||||||
|  |       add kid to leaves | ||||||
|  |    else /* done with the derivation, no more kids */ | ||||||
|  |       // the derivation is reachable, otherwise it was reset in another branch | ||||||
|  |       p = parent of n | ||||||
|  |       p.reset_derivation () | ||||||
|  |       add p to the leaves | ||||||
|  | 
 | ||||||
|  |       | ||||||
|  | ================================================================================= | ||||||
|  | create derivation for the top of d | ||||||
|  | input:  | ||||||
|  |        model M,  | ||||||
|  |        transition relation formula trans with auxiliary variables quantified out | ||||||
|  |        sequence of pedicates P_i,  | ||||||
|  |        may and must summaries of P_i | ||||||
|  | ================================================================================= | ||||||
|  | 
 | ||||||
|  | create first derivation child: | ||||||
|  |        input: model  | ||||||
|  |         | ||||||
|  |         | ||||||
|  | create next derivation child: | ||||||
|  |        create new model | ||||||
|  |        update trans by computing pre-image over new reachability facts | ||||||
|  |        call create next derivation child | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | create next derivation child using a given model, and starting index | ||||||
|  | 
 | ||||||
|  | ========================================================= | ||||||
|  | 
 | ||||||
|  | create a next model for a derivation  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- | ||||||
|  | 
 | ||||||
|  | // an obligation | ||||||
|  | model_node | ||||||
|  |   // NULL means root | ||||||
|  |   model_node_ref parent | ||||||
|  |   model_node_ref_vector kids | ||||||
|  |    | ||||||
|  |   pred_transformer &predicate | ||||||
|  |   expr* condition | ||||||
|  |   unsigned level  | ||||||
|  |   unsigned depth   | ||||||
|  |   // monotonically increasing | ||||||
|  |   unsigned id     | ||||||
|  |    | ||||||
|  |   bool open; | ||||||
|  |    | ||||||
|  | 
 | ||||||
|  | model_node::close () | ||||||
|  |   open = false | ||||||
|  |   for k : kids do k.close () | ||||||
|  | 
 | ||||||
|  | model_search | ||||||
|  |    | ||||||
|  |   model_node_ref root; | ||||||
|  |    | ||||||
|  |   // proof obligations | ||||||
|  |   priority_queue m_obligations; | ||||||
|  |   model_node_ref m_last_reachable; | ||||||
|  |    | ||||||
|  |    | ||||||
|  | bool model_node::operator< (model_node& other) | ||||||
|  |    lexicographic order based on  | ||||||
|  |    level<, depth<, id> | ||||||
|  |    | ||||||
|  |    | ||||||
|  | 
 | ||||||
|  | assert (!m_last_reachable); | ||||||
|  | while (!m_obligations.empty ()) | ||||||
|  | { | ||||||
|  |    // propagate reachability as much as possible | ||||||
|  |    while (m_last_reachable) | ||||||
|  |    { | ||||||
|  |      obl = m_last_reachable | ||||||
|  |      m_last_reachable.reset (); | ||||||
|  |      if (is_root (obl)) return true; | ||||||
|  |      if (discharge_obligation (obl.get_parent ()) == l_true) | ||||||
|  |         m_last_reachable = obl.get_parent (); | ||||||
|  |    } | ||||||
|  |     | ||||||
|  |    // at least one obligation is not closed, ow root was reachable         | ||||||
|  |    while (m_obligations.top ().is_closed ()) m_obligations.pop (); | ||||||
|  |    assert (!m_obligations.empty ());                             | ||||||
|  |            | ||||||
|  |    // process an obligation | ||||||
|  |    assert (!m_last_reachable) | ||||||
|  |    obl = m_obligations.top (); | ||||||
|  |    switch (discharge_obligation (obl)) | ||||||
|  |    {  | ||||||
|  |      case l_true:  | ||||||
|  |         // if reachable, schedule a reachability round | ||||||
|  |         m_last_reachable = m_obligations.top (); | ||||||
|  |         m_obligations.pop (); | ||||||
|  |         break; | ||||||
|  |      case l_false: | ||||||
|  |         // if unreachable removed from the queue | ||||||
|  |         m_obligations.pop (); | ||||||
|  |         /// bump level  | ||||||
|  |         obl.inc_level (); | ||||||
|  |         /// optionally insert back into the queue | ||||||
|  |         if (is_active (obl)) m_obligations.push (obl); | ||||||
|  |         break; | ||||||
|  |      default: | ||||||
|  |         assert (m_obligations.top () != obl); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | return false | ||||||
|  | 
 | ||||||
|  | /// with priority queue | ||||||
|  | bool is_active (model_node obl) { return level <= m_search.max_level (); } | ||||||
|  | /// with out priority queue. Discharged obligations are dropped | ||||||
|  | bool is_active (model_node obl) { return false; } | ||||||
|  | 
 | ||||||
|  | discharge_obligation (model_node obl) | ||||||
|  | { | ||||||
|  |   assert (!obl.is_closed ()); | ||||||
|  |   switch (check_reachability (obl)) | ||||||
|  |   { | ||||||
|  |     case l_true: | ||||||
|  |        obl.close () | ||||||
|  |        update reachability facts | ||||||
|  |        return l_true; | ||||||
|  |     case l_false: | ||||||
|  |        update lemmas  | ||||||
|  |        return l_false | ||||||
|  |     case l_unknown: | ||||||
|  |       create children | ||||||
|  |       populate m_obligations queue | ||||||
|  |       return l_unknown | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ============================================================= | ||||||
|  | 
 | ||||||
|  | a node keeps a derivation object | ||||||
|  | 
 | ||||||
|  | if a node is sat, a new node is constructed and inherits the derivation object | ||||||
|  | if a node is sat and the derivation is done, this is reported to the parent | ||||||
|  | 
 | ||||||
|  | expand_node(n): | ||||||
|  |   process node ignoring derivation | ||||||
|  |   if sat: | ||||||
|  |       if concrete: | ||||||
|  |          if has derivation and has next child | ||||||
|  |             close current node and push new node | ||||||
|  |             return l_undef | ||||||
|  |          else | ||||||
|  |              return l_true | ||||||
|  |       else | ||||||
|  |          create_child (creates a new node and optionally sets derivation) | ||||||
|  |   else if unsat | ||||||
|  |       generate lemmas | ||||||
|  |       derivation remains unchanged to be used at a higher level | ||||||
|  |       return | ||||||
|  | ====================================================================== | ||||||
|  | 1. open disjunction for transition relation | ||||||
|  |    - a fresh literal to open the disjunction of the transition relation | ||||||
|  |    - expr* expand_init (expr *e) -- add e to initial state and return | ||||||
|  |      new disj var | ||||||
|  |    - close the disjunction by passing the negation of the literal | ||||||
|  |      during various calls | ||||||
|  |    - store the literal negated to have access to both positive and | ||||||
|  |      negative versions | ||||||
|  |    - with this, can do an optional check whether the lemmas alone are | ||||||
|  |      strong enough to discharge the counterexample. Easiest is to | ||||||
|  |      implement it as a separate pre-check. | ||||||
|  |       | ||||||
|  | 2. auxiliary variables in lemmas and reach-facts.  | ||||||
|  |    - store and expect auxiliary variables | ||||||
|  |    - quantify them out when necessary | ||||||
|  | 
 | ||||||
|  | 3. initial rules as reach-facts | ||||||
|  |    - add initial rules of a predicate to its reach-facts. Propagate them to uses. | ||||||
|  |    - this way, the checks at level 0 will include initial rules of | ||||||
|  |      immediate predecessors | ||||||
|  | ====================================================================== | ||||||
|  | 
 | ||||||
|  | reach_fact_ref_vector m_reach_facts | ||||||
|  | app_ref_vector m_reach_case_vars | ||||||
|  | 
 | ||||||
|  | bool is_must_reachable (expr *state, model_ref *model) | ||||||
|  | reach_fact* get_used_reach_fact (model_evaluator &mev) | ||||||
|  | app* mk_fresh_reach_case_var () | ||||||
|  | expr* get_reach () | ||||||
|  | expr* get_last_reach_case_var () | ||||||
|  | app* get_reach_case_var (unsigned idx) | ||||||
|  | 
 | ||||||
|  | get_used_origin_reach_fact(): | ||||||
|  |      | ||||||
|  | 
 | ||||||
|  | ====================================================================== | ||||||
|  | 4. track relationship between an obligation and lemmas. Attempt to | ||||||
|  |    generalize an obligation into the exact lemma that worked | ||||||
|  |    before. Perhaps pick one lemma with highest level? Implement as | ||||||
|  |    core-generalizer. Will require reworking how legacy_frames is implemented. | ||||||
							
								
								
									
										332
									
								
								src/muz/spacer/spacer_proof_utils.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								src/muz/spacer/spacer_proof_utils.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,332 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_proof_utils.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |     Utilities to traverse and manipulate proofs | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "spacer_proof_utils.h" | ||||||
|  | #include "ast_util.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | 
 | ||||||
|  | #include "proof_checker.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | ProofIteratorPostOrder::ProofIteratorPostOrder(proof* root, ast_manager& manager) : m(manager) | ||||||
|  | {m_todo.push_back(root);} | ||||||
|  | 
 | ||||||
|  | bool ProofIteratorPostOrder::hasNext() | ||||||
|  | {return !m_todo.empty();} | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * iterative post-order depth-first search (DFS) through the proof DAG | ||||||
|  |  */ | ||||||
|  | proof* ProofIteratorPostOrder::next() | ||||||
|  | { | ||||||
|  |     while (!m_todo.empty()) { | ||||||
|  |         proof* currentNode = m_todo.back(); | ||||||
|  | 
 | ||||||
|  |         // if we haven't already visited the current unit
 | ||||||
|  |         if (!m_visited.is_marked(currentNode)) { | ||||||
|  |             bool existsUnvisitedParent = false; | ||||||
|  | 
 | ||||||
|  |             // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result
 | ||||||
|  |             // for currentProof now, but wait until those unprocessed premises are processed.
 | ||||||
|  |             for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { | ||||||
|  |                 SASSERT(m.is_proof(currentNode->get_arg(i))); | ||||||
|  |                 proof* premise = to_app(currentNode->get_arg(i)); | ||||||
|  | 
 | ||||||
|  |                 // if we haven't visited the current premise yet
 | ||||||
|  |                 if (!m_visited.is_marked(premise)) { | ||||||
|  |                     // add it to the stack
 | ||||||
|  |                     m_todo.push_back(premise); | ||||||
|  |                     existsUnvisitedParent = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // if we already visited all parent-inferences, we can visit the inference too
 | ||||||
|  |             if (!existsUnvisitedParent) { | ||||||
|  |                 m_visited.mark(currentNode, true); | ||||||
|  |                 m_todo.pop_back(); | ||||||
|  |                 return currentNode; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             m_todo.pop_back(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // we have already iterated through all inferences
 | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class reduce_hypotheses { | ||||||
|  |     ast_manager &m; | ||||||
|  |     // tracking all created expressions
 | ||||||
|  |     expr_ref_vector m_pinned; | ||||||
|  | 
 | ||||||
|  |     // cache for the transformation
 | ||||||
|  |     obj_map<proof, proof*> m_cache; | ||||||
|  | 
 | ||||||
|  |     // map from unit literals to their hypotheses-free derivations
 | ||||||
|  |     obj_map<expr, proof*> m_units; | ||||||
|  | 
 | ||||||
|  |     // -- all hypotheses in the the proof
 | ||||||
|  |     obj_hashtable<expr> m_hyps; | ||||||
|  | 
 | ||||||
|  |     // marks hypothetical proofs
 | ||||||
|  |     ast_mark m_hypmark; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // stack
 | ||||||
|  |     ptr_vector<proof> m_todo; | ||||||
|  | 
 | ||||||
|  |     void reset() | ||||||
|  |     { | ||||||
|  |         m_cache.reset(); | ||||||
|  |         m_units.reset(); | ||||||
|  |         m_hyps.reset(); | ||||||
|  |         m_hypmark.reset(); | ||||||
|  |         m_pinned.reset(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool compute_mark1(proof *pr) | ||||||
|  |     { | ||||||
|  |         bool hyp_mark = false; | ||||||
|  |         // lemmas clear all hypotheses
 | ||||||
|  |         if (!m.is_lemma(pr)) { | ||||||
|  |             for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) { | ||||||
|  |                 if (m_hypmark.is_marked(m.get_parent(pr, i))) { | ||||||
|  |                     hyp_mark = true; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         m_hypmark.mark(pr, hyp_mark); | ||||||
|  |         return hyp_mark; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void compute_marks(proof* pr) | ||||||
|  |     { | ||||||
|  |         proof *p; | ||||||
|  |         ProofIteratorPostOrder pit(pr, m); | ||||||
|  |         while (pit.hasNext()) { | ||||||
|  |             p = pit.next(); | ||||||
|  |             if (m.is_hypothesis(p)) { | ||||||
|  |                 m_hypmark.mark(p, true); | ||||||
|  |                 m_hyps.insert(m.get_fact(p)); | ||||||
|  |             } else { | ||||||
|  |                 bool hyp_mark = compute_mark1(p); | ||||||
|  |                 // collect units that are hyp-free and are used as hypotheses somewhere
 | ||||||
|  |                 if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) | ||||||
|  |                 { m_units.insert(m.get_fact(p), p); } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void find_units(proof *pr) | ||||||
|  |     { | ||||||
|  |         // optional. not implemented yet.
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void reduce(proof* pf, proof_ref &out) | ||||||
|  |     { | ||||||
|  |         proof *res = NULL; | ||||||
|  | 
 | ||||||
|  |         m_todo.reset(); | ||||||
|  |         m_todo.push_back(pf); | ||||||
|  |         ptr_buffer<proof> args; | ||||||
|  |         bool dirty = false; | ||||||
|  | 
 | ||||||
|  |         while (!m_todo.empty()) { | ||||||
|  |             proof *p, *tmp, *pp; | ||||||
|  |             unsigned todo_sz; | ||||||
|  | 
 | ||||||
|  |             p = m_todo.back(); | ||||||
|  |             if (m_cache.find(p, tmp)) { | ||||||
|  |                 res = tmp; | ||||||
|  |                 m_todo.pop_back(); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             dirty = false; | ||||||
|  |             args.reset(); | ||||||
|  |             todo_sz = m_todo.size(); | ||||||
|  |             for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { | ||||||
|  |                 pp = m.get_parent(p, i); | ||||||
|  |                 if (m_cache.find(pp, tmp)) { | ||||||
|  |                     args.push_back(tmp); | ||||||
|  |                     dirty = dirty || pp != tmp; | ||||||
|  |                 } else { | ||||||
|  |                     m_todo.push_back(pp); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (todo_sz < m_todo.size()) { continue; } | ||||||
|  |             else { m_todo.pop_back(); } | ||||||
|  | 
 | ||||||
|  |             if (m.is_hypothesis(p)) { | ||||||
|  |                 // hyp: replace by a corresponding unit
 | ||||||
|  |                 if (m_units.find(m.get_fact(p), tmp)) { | ||||||
|  |                     res = tmp; | ||||||
|  |                 } else { res = p; } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             else if (!dirty) { res = p; } | ||||||
|  | 
 | ||||||
|  |             else if (m.is_lemma(p)) { | ||||||
|  |                 //lemma: reduce the premise; remove reduced consequences from conclusion
 | ||||||
|  |                 SASSERT(args.size() == 1); | ||||||
|  |                 res = mk_lemma_core(args.get(0), m.get_fact(p)); | ||||||
|  |                 compute_mark1(res); | ||||||
|  |             } else if (m.is_unit_resolution(p)) { | ||||||
|  |                 // unit: reduce untis; reduce the first premise; rebuild unit resolution
 | ||||||
|  |                 res = mk_unit_resolution_core(args.size(), args.c_ptr()); | ||||||
|  |                 compute_mark1(res); | ||||||
|  |             } else  { | ||||||
|  |                 // other: reduce all premises; reapply
 | ||||||
|  |                 if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } | ||||||
|  |                 SASSERT(p->get_decl()->get_arity() == args.size()); | ||||||
|  |                 res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); | ||||||
|  |                 m_pinned.push_back(res); | ||||||
|  |                 compute_mark1(res); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             SASSERT(res); | ||||||
|  |             m_cache.insert(p, res); | ||||||
|  | 
 | ||||||
|  |             if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         out = res; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // returns true if (hypothesis (not a)) would be reduced
 | ||||||
|  |     bool is_reduced(expr *a) | ||||||
|  |     { | ||||||
|  |         expr_ref e(m); | ||||||
|  |         if (m.is_not(a)) { e = to_app(a)->get_arg(0); } | ||||||
|  |         else { e = m.mk_not(a); } | ||||||
|  | 
 | ||||||
|  |         return m_units.contains(e); | ||||||
|  |     } | ||||||
|  |     proof *mk_lemma_core(proof *pf, expr *fact) | ||||||
|  |     { | ||||||
|  |         ptr_buffer<expr> args; | ||||||
|  |         expr_ref lemma(m); | ||||||
|  | 
 | ||||||
|  |         if (m.is_or(fact)) { | ||||||
|  |             for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) { | ||||||
|  |                 expr *a = to_app(fact)->get_arg(i); | ||||||
|  |                 if (!is_reduced(a)) | ||||||
|  |                 { args.push_back(a); } | ||||||
|  |             } | ||||||
|  |         } else if (!is_reduced(fact)) | ||||||
|  |         { args.push_back(fact); } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         if (args.size() == 0) { return pf; } | ||||||
|  |         else if (args.size() == 1) { | ||||||
|  |             lemma = args.get(0); | ||||||
|  |         } else { | ||||||
|  |             lemma = m.mk_or(args.size(), args.c_ptr()); | ||||||
|  |         } | ||||||
|  |         proof* res = m.mk_lemma(pf, lemma); | ||||||
|  |         m_pinned.push_back(res); | ||||||
|  | 
 | ||||||
|  |         if (m_hyps.contains(lemma)) | ||||||
|  |         { m_units.insert(lemma, res); } | ||||||
|  |         return res; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     proof *mk_unit_resolution_core(unsigned num_args, proof* const *args) | ||||||
|  |     { | ||||||
|  | 
 | ||||||
|  |         ptr_buffer<proof> pf_args; | ||||||
|  |         pf_args.push_back(args [0]); | ||||||
|  | 
 | ||||||
|  |         app *cls_fact = to_app(m.get_fact(args[0])); | ||||||
|  |         ptr_buffer<expr> cls; | ||||||
|  |         if (m.is_or(cls_fact)) { | ||||||
|  |             for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) | ||||||
|  |             { cls.push_back(cls_fact->get_arg(i)); } | ||||||
|  |         } else { cls.push_back(cls_fact); } | ||||||
|  | 
 | ||||||
|  |         // construct new resovent
 | ||||||
|  |         ptr_buffer<expr> new_fact_cls; | ||||||
|  |         bool found; | ||||||
|  |         // XXX quadratic
 | ||||||
|  |         for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { | ||||||
|  |             found = false; | ||||||
|  |             for (unsigned j = 1; j < num_args; ++j) { | ||||||
|  |                 if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { | ||||||
|  |                     found = true; | ||||||
|  |                     pf_args.push_back(args [j]); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (!found) { | ||||||
|  |                 new_fact_cls.push_back(cls.get(i)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); | ||||||
|  |         expr_ref new_fact(m); | ||||||
|  |         new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); | ||||||
|  | 
 | ||||||
|  |         // create new proof step
 | ||||||
|  |         proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); | ||||||
|  |         m_pinned.push_back(res); | ||||||
|  |         return res; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // reduce all units, if any unit reduces to false return true and put its proof into out
 | ||||||
|  |     bool reduce_units(proof_ref &out) | ||||||
|  |     { | ||||||
|  |         proof_ref res(m); | ||||||
|  |         for (auto entry : m_units) { | ||||||
|  |             reduce(entry.get_value(), res); | ||||||
|  |             if (m.is_false(m.get_fact(res))) { | ||||||
|  |                 out = res; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             res.reset(); | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void operator()(proof_ref &pr) | ||||||
|  |     { | ||||||
|  |         compute_marks(pr); | ||||||
|  |         if (!reduce_units(pr)) { | ||||||
|  |             reduce(pr.get(), pr); | ||||||
|  |         } | ||||||
|  |         reset(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | void reduce_hypotheses(proof_ref &pr) | ||||||
|  | { | ||||||
|  |     ast_manager &m = pr.get_manager(); | ||||||
|  |     class reduce_hypotheses hypred(m); | ||||||
|  |     hypred(pr); | ||||||
|  |     DEBUG_CODE(proof_checker pc(m); | ||||||
|  |                expr_ref_vector side(m); | ||||||
|  |                SASSERT(pc.check(pr, side)); | ||||||
|  |               ); | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										43
									
								
								src/muz/spacer/spacer_proof_utils.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/muz/spacer/spacer_proof_utils.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_proof_utils.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |     Utilities to traverse and manipulate proofs | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_PROOF_UTILS_H_ | ||||||
|  | #define _SPACER_PROOF_UTILS_H_ | ||||||
|  | #include "ast.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | /*
 | ||||||
|  |  * iterator, which traverses the proof in depth-first post-order. | ||||||
|  |  */ | ||||||
|  | class ProofIteratorPostOrder { | ||||||
|  | public: | ||||||
|  |     ProofIteratorPostOrder(proof* refutation, ast_manager& manager); | ||||||
|  |     bool hasNext(); | ||||||
|  |     proof* next(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ptr_vector<proof> m_todo; | ||||||
|  |     ast_mark m_visited; // the proof nodes we have already visited
 | ||||||
|  | 
 | ||||||
|  |     ast_manager& m; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void reduce_hypotheses(proof_ref &pr); | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										298
									
								
								src/muz/spacer/spacer_prop_solver.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								src/muz/spacer/spacer_prop_solver.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,298 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_prop_solver.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     SAT solver abstraction for SPACER. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  |     Anvesh Komuravelli | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include <sstream> | ||||||
|  | #include "model.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "spacer_prop_solver.h" | ||||||
|  | #include "ast_smt2_pp.h" | ||||||
|  | #include "dl_util.h" | ||||||
|  | #include "model_pp.h" | ||||||
|  | #include "smt_params.h" | ||||||
|  | #include "datatype_decl_plugin.h" | ||||||
|  | #include "bv_decl_plugin.h" | ||||||
|  | #include "spacer_farkas_learner.h" | ||||||
|  | #include "ast_smt2_pp.h" | ||||||
|  | #include "expr_replacer.h" | ||||||
|  | #include "fixedpoint_params.hpp" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) : | ||||||
|  |     m(pm.get_manager()), | ||||||
|  |     m_pm(pm), | ||||||
|  |     m_name(name), | ||||||
|  |     m_ctx(NULL), | ||||||
|  |     m_pos_level_atoms(m), | ||||||
|  |     m_neg_level_atoms(m), | ||||||
|  |     m_core(0), | ||||||
|  |     m_subset_based_core(false), | ||||||
|  |     m_uses_level(infty_level()), | ||||||
|  |     m_delta_level(false), | ||||||
|  |     m_in_level(false), | ||||||
|  |     m_use_push_bg(p.spacer_keep_proxy()) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     m_solvers[0] = pm.mk_fresh(); | ||||||
|  |     m_fparams[0] = &pm.fparams(); | ||||||
|  | 
 | ||||||
|  |     m_solvers[1] = pm.mk_fresh2(); | ||||||
|  |     m_fparams[1] = &pm.fparams2(); | ||||||
|  | 
 | ||||||
|  |     m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); | ||||||
|  |     m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0; i < 2; ++i) | ||||||
|  |     { m_contexts[i]->assert_expr(m_pm.get_background()); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::add_level() | ||||||
|  | { | ||||||
|  |     unsigned idx = level_cnt(); | ||||||
|  |     std::stringstream name; | ||||||
|  |     name << m_name << "#level_" << idx; | ||||||
|  |     func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, 0, m.mk_bool_sort()); | ||||||
|  |     m_level_preds.push_back(lev_pred); | ||||||
|  | 
 | ||||||
|  |     app_ref pos_la(m.mk_const(lev_pred), m); | ||||||
|  |     app_ref neg_la(m.mk_not(pos_la.get()), m); | ||||||
|  | 
 | ||||||
|  |     m_pos_level_atoms.push_back(pos_la); | ||||||
|  |     m_neg_level_atoms.push_back(neg_la); | ||||||
|  | 
 | ||||||
|  |     m_level_atoms_set.insert(pos_la.get()); | ||||||
|  |     m_level_atoms_set.insert(neg_la.get()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::ensure_level(unsigned lvl) | ||||||
|  | { | ||||||
|  |     while (lvl >= level_cnt()) { | ||||||
|  |         add_level(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsigned prop_solver::level_cnt() const | ||||||
|  | { | ||||||
|  |     return m_level_preds.size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::assert_level_atoms(unsigned level) | ||||||
|  | { | ||||||
|  |     unsigned lev_cnt = level_cnt(); | ||||||
|  |     for (unsigned i = 0; i < lev_cnt; i++) { | ||||||
|  |         bool active = m_delta_level ? i == level : i >= level; | ||||||
|  |         app * lev_atom = | ||||||
|  |             active ? m_neg_level_atoms.get(i) : m_pos_level_atoms.get(i); | ||||||
|  |         m_ctx->push_bg(lev_atom); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::assert_expr(expr * form) | ||||||
|  | { | ||||||
|  |     SASSERT(!m_in_level); | ||||||
|  |     m_contexts[0]->assert_expr(form); | ||||||
|  |     m_contexts[1]->assert_expr(form); | ||||||
|  |     IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";); | ||||||
|  |     TRACE("spacer", tout << "add_formula: " << mk_pp(form, m) << "\n";); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::assert_expr(expr * form, unsigned level) | ||||||
|  | { | ||||||
|  |     ensure_level(level); | ||||||
|  |     app * lev_atom = m_pos_level_atoms[level].get(); | ||||||
|  |     app_ref lform(m.mk_or(form, lev_atom), m); | ||||||
|  |     assert_expr(lform); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /// Poor man's maxsat. No guarantees of maximum solution
 | ||||||
|  | /// Runs maxsat loop on m_ctx Returns l_false if hard is unsat,
 | ||||||
|  | /// otherwise reduces soft such that hard & soft is sat.
 | ||||||
|  | lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) | ||||||
|  | { | ||||||
|  |     // replace expressions by assumption literals
 | ||||||
|  |     itp_solver::scoped_mk_proxy _p_(*m_ctx, hard); | ||||||
|  |     unsigned hard_sz = hard.size(); | ||||||
|  |     // assume soft constraints are propositional literals (no need to proxy)
 | ||||||
|  |     hard.append(soft); | ||||||
|  | 
 | ||||||
|  |     lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr()); | ||||||
|  |     // if hard constraints alone are unsat or there are no soft
 | ||||||
|  |     // constraints, we are done
 | ||||||
|  |     if (res != l_false || soft.empty()) { return res; } | ||||||
|  | 
 | ||||||
|  |     // clear soft constraints, we will recompute them later
 | ||||||
|  |     soft.reset(); | ||||||
|  | 
 | ||||||
|  |     expr_ref saved(m); | ||||||
|  |     ptr_vector<expr> core; | ||||||
|  |     m_ctx->get_unsat_core(core); | ||||||
|  | 
 | ||||||
|  |     // while there are soft constraints
 | ||||||
|  |     while (hard.size() > hard_sz) { | ||||||
|  |         bool found = false; | ||||||
|  |         // look for a soft constraint that is in the unsat core
 | ||||||
|  |         for (unsigned i = hard_sz, sz = hard.size(); i < sz; ++i) | ||||||
|  |             if (core.contains(hard.get(i))) { | ||||||
|  |                 found = true; | ||||||
|  |                 // AG: not sure why we are saving it
 | ||||||
|  |                 saved = hard.get(i); | ||||||
|  |                 hard[i] = hard.back(); | ||||||
|  |                 hard.pop_back(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         // if no soft constraints in the core, return this should
 | ||||||
|  |         // not happen because it implies that hard alone is unsat
 | ||||||
|  |         // and that is taken care of earlier
 | ||||||
|  |         if (!found) { | ||||||
|  |             hard.resize(hard_sz); | ||||||
|  |             return l_false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // check that the NEW constraints became sat
 | ||||||
|  |         res = m_ctx->check_sat(hard.size(), hard.c_ptr()); | ||||||
|  |         if (res != l_false) { break; } | ||||||
|  |         // still unsat, update the core and repeat
 | ||||||
|  |         core.reset(); | ||||||
|  |         m_ctx->get_unsat_core(core); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // update soft with found soft constraints
 | ||||||
|  |     if (res == l_true) { | ||||||
|  |         for (unsigned i = hard_sz, sz = hard.size(); i < sz; ++i) | ||||||
|  |         { soft.push_back(hard.get(i)); } | ||||||
|  |     } | ||||||
|  |     // revert hard back to the right size
 | ||||||
|  |     // proxies are undone on exit via scoped_mk_proxy
 | ||||||
|  |     hard.resize(hard_sz); | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | lbool prop_solver::internal_check_assumptions( | ||||||
|  |     expr_ref_vector& hard_atoms, | ||||||
|  |     expr_ref_vector& soft_atoms) | ||||||
|  | { | ||||||
|  |     // XXX Turn model generation if m_model != 0
 | ||||||
|  |     SASSERT(m_ctx); | ||||||
|  |     SASSERT(m_ctx_fparams); | ||||||
|  |     flet<bool> _model(m_ctx_fparams->m_model, m_model != 0); | ||||||
|  | 
 | ||||||
|  |     if (m_in_level) { assert_level_atoms(m_current_level); } | ||||||
|  |     lbool result = maxsmt(hard_atoms, soft_atoms); | ||||||
|  |     if (result != l_false && m_model) { m_ctx->get_model(*m_model); } | ||||||
|  | 
 | ||||||
|  |     SASSERT(result != l_false || soft_atoms.empty()); | ||||||
|  | 
 | ||||||
|  |     /// compute level used in the core
 | ||||||
|  |     // XXX this is a poor approximation because the core will get minimized further
 | ||||||
|  |     if (result == l_false) { | ||||||
|  |         ptr_vector<expr> core; | ||||||
|  |         m_ctx->get_full_unsat_core(core); | ||||||
|  |         unsigned core_size = core.size(); | ||||||
|  |         m_uses_level = infty_level(); | ||||||
|  | 
 | ||||||
|  |         for (unsigned i = 0; i < core_size; ++i) { | ||||||
|  |             if (m_level_atoms_set.contains(core[i])) { | ||||||
|  |                 unsigned sz = std::min(m_uses_level, m_neg_level_atoms.size()); | ||||||
|  |                 for (unsigned j = 0; j < sz; ++j) | ||||||
|  |                     if (m_neg_level_atoms [j].get() == core[i]) { | ||||||
|  |                         m_uses_level = j; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 SASSERT(!is_infty_level(m_uses_level)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { | ||||||
|  |         TRACE("spacer", tout << "theory core\n";); | ||||||
|  |         m_core->reset(); | ||||||
|  |         m_ctx->get_itp_core(*m_core); | ||||||
|  |     } else if (result == l_false && m_core) { | ||||||
|  |         m_core->reset(); | ||||||
|  |         m_ctx->get_unsat_core(*m_core); | ||||||
|  |         // manually undo proxies because maxsmt() call above manually adds proxies
 | ||||||
|  |         m_ctx->undo_proxies(*m_core); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, | ||||||
|  |                                      expr_ref_vector& soft, | ||||||
|  |                                      unsigned num_bg, expr * const * bg, | ||||||
|  |                                      unsigned solver_id) | ||||||
|  | { | ||||||
|  |     // current clients expect that flattening of HARD  is
 | ||||||
|  |     // done implicitly during check_assumptions
 | ||||||
|  |     expr_ref_vector hard(m); | ||||||
|  |     hard.append(_hard.size(), _hard.c_ptr()); | ||||||
|  |     flatten_and(hard); | ||||||
|  | 
 | ||||||
|  |     m_ctx = m_contexts [solver_id == 0 ? 0 : 0 /* 1 */].get(); | ||||||
|  |     m_ctx_fparams = m_fparams [solver_id == 0 ? 0 : 0 /* 1 */]; | ||||||
|  | 
 | ||||||
|  |     // can be disabled if use_push_bg == true
 | ||||||
|  |     // solver::scoped_push _s_(*m_ctx);
 | ||||||
|  |     if (!m_use_push_bg) { m_ctx->push(); } | ||||||
|  |     itp_solver::scoped_bg _b_(*m_ctx); | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0; i < num_bg; ++i) | ||||||
|  |         if (m_use_push_bg) { m_ctx->push_bg(bg [i]); } | ||||||
|  |         else { m_ctx->assert_expr(bg[i]); } | ||||||
|  | 
 | ||||||
|  |     unsigned soft_sz = soft.size(); | ||||||
|  | #pragma unused(soft_sz) | ||||||
|  |     lbool res = internal_check_assumptions(hard, soft); | ||||||
|  |     if (!m_use_push_bg) { m_ctx->pop(1); } | ||||||
|  | 
 | ||||||
|  |     TRACE("psolve_verbose", | ||||||
|  |           tout << "sat: " << mk_pp(mk_and(hard), m) << "\n" | ||||||
|  |           << mk_pp(mk_and(soft), m) << "\n"; | ||||||
|  |           for (unsigned i = 0; i < num_bg; ++i) | ||||||
|  |           tout << "bg" << i << ": " << mk_pp(bg[i], m) << "\n"; | ||||||
|  |           tout << "res: " << res << "\n";); | ||||||
|  |     CTRACE("psolve", m_core, | ||||||
|  |            tout << "core is: " << mk_pp(mk_and(*m_core), m) << "\n";); | ||||||
|  | 
 | ||||||
|  |     SASSERT(soft_sz >= soft.size()); | ||||||
|  | 
 | ||||||
|  |     // -- reset all parameters
 | ||||||
|  |     m_core = 0; | ||||||
|  |     m_model = 0; | ||||||
|  |     m_subset_based_core = false; | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::collect_statistics(statistics& st) const | ||||||
|  | { | ||||||
|  |     m_contexts[0]->collect_statistics(st); | ||||||
|  |     m_contexts[1]->collect_statistics(st); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void prop_solver::reset_statistics() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										143
									
								
								src/muz/spacer/spacer_prop_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/muz/spacer/spacer_prop_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,143 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_prop_solver.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     SAT solver abstraction for SPACER. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _PROP_SOLVER_H_ | ||||||
|  | #define _PROP_SOLVER_H_ | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  | #include "ast.h" | ||||||
|  | #include "obj_hashtable.h" | ||||||
|  | #include "smt_kernel.h" | ||||||
|  | #include "util.h" | ||||||
|  | #include "vector.h" | ||||||
|  | #include "spacer_manager.h" | ||||||
|  | #include "spacer_smt_context_manager.h" | ||||||
|  | #include "spacer_itp_solver.h" | ||||||
|  | 
 | ||||||
|  | struct fixedpoint_params; | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class prop_solver { | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ast_manager&        m; | ||||||
|  |     manager&            m_pm; | ||||||
|  |     symbol              m_name; | ||||||
|  |     smt_params*         m_fparams[2]; | ||||||
|  |     solver*             m_solvers[2]; | ||||||
|  |     scoped_ptr<itp_solver>  m_contexts[2]; | ||||||
|  |     itp_solver *            m_ctx; | ||||||
|  |     smt_params *            m_ctx_fparams; | ||||||
|  |     decl_vector         m_level_preds; | ||||||
|  |     app_ref_vector      m_pos_level_atoms;  // atoms used to identify level
 | ||||||
|  |     app_ref_vector      m_neg_level_atoms;  //
 | ||||||
|  |     obj_hashtable<expr> m_level_atoms_set; | ||||||
|  |     expr_ref_vector*    m_core; | ||||||
|  |     model_ref*          m_model; | ||||||
|  |     bool                m_subset_based_core; | ||||||
|  |     unsigned            m_uses_level; | ||||||
|  |     /// if true sets the solver into a delta level, enabling only
 | ||||||
|  |     /// atoms explicitly asserted in m_current_level
 | ||||||
|  |     bool                m_delta_level; | ||||||
|  |     bool                m_in_level; | ||||||
|  |     bool                m_use_push_bg; | ||||||
|  |     unsigned            m_current_level;    // set when m_in_level
 | ||||||
|  | 
 | ||||||
|  |     void assert_level_atoms(unsigned level); | ||||||
|  | 
 | ||||||
|  |     void ensure_level(unsigned lvl); | ||||||
|  | 
 | ||||||
|  |     lbool internal_check_assumptions(expr_ref_vector &hard, | ||||||
|  |                                      expr_ref_vector &soft); | ||||||
|  | 
 | ||||||
|  |     lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     prop_solver(spacer::manager& pm, fixedpoint_params const& p, symbol const& name); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void set_core(expr_ref_vector* core) { m_core = core; } | ||||||
|  |     void set_model(model_ref* mdl) { m_model = mdl; } | ||||||
|  |     void set_subset_based_core(bool f) { m_subset_based_core = f; } | ||||||
|  |     bool assumes_level() const { return !is_infty_level(m_uses_level); } | ||||||
|  |     unsigned uses_level() const {return m_uses_level;} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void add_level(); | ||||||
|  |     unsigned level_cnt() const; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void assert_expr(expr * form); | ||||||
|  |     void assert_expr(expr * form, unsigned level); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * check assumptions with a background formula | ||||||
|  |      */ | ||||||
|  |     lbool check_assumptions(const expr_ref_vector & hard, | ||||||
|  |                             expr_ref_vector & soft, | ||||||
|  |                             unsigned num_bg = 0, | ||||||
|  |                             expr * const *bg = NULL, | ||||||
|  |                             unsigned solver_id = 0); | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const; | ||||||
|  |     void reset_statistics(); | ||||||
|  | 
 | ||||||
|  |     class scoped_level { | ||||||
|  |         bool& m_lev; | ||||||
|  |     public: | ||||||
|  |         scoped_level(prop_solver& ps, unsigned lvl): m_lev(ps.m_in_level) | ||||||
|  |         { | ||||||
|  |             SASSERT(!m_lev); | ||||||
|  |             m_lev = true; | ||||||
|  |             ps.m_current_level = lvl; | ||||||
|  |         } | ||||||
|  |         ~scoped_level() { m_lev = false; } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class scoped_subset_core { | ||||||
|  |         prop_solver &m_ps; | ||||||
|  |         bool m_subset_based_core; | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         scoped_subset_core(prop_solver &ps, bool subset_core) : | ||||||
|  |             m_ps(ps), m_subset_based_core(ps.m_subset_based_core) | ||||||
|  |         {m_ps.set_subset_based_core(subset_core);} | ||||||
|  | 
 | ||||||
|  |         ~scoped_subset_core() | ||||||
|  |         {m_ps.set_subset_based_core(m_subset_based_core);} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class scoped_delta_level : public scoped_level { | ||||||
|  |         bool &m_delta; | ||||||
|  |     public: | ||||||
|  |         scoped_delta_level(prop_solver &ps, unsigned lvl) : | ||||||
|  |             scoped_level(ps, lvl), m_delta(ps.m_delta_level) {m_delta = true;} | ||||||
|  |         ~scoped_delta_level() {m_delta = false;} | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										2333
									
								
								src/muz/spacer/spacer_qe_project.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2333
									
								
								src/muz/spacer/spacer_qe_project.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										49
									
								
								src/muz/spacer/spacer_qe_project.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/muz/spacer/spacer_qe_project.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_qe_project.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Model-based projection | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Anvesh Komuravelli | ||||||
|  |     Arie Gurfinkel (arie) | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef SPACER_QE_PROJECT_H_ | ||||||
|  | #define SPACER_QE_PROJECT_H_ | ||||||
|  | 
 | ||||||
|  | #include "model.h" | ||||||
|  | #include "expr_map.h" | ||||||
|  | 
 | ||||||
|  | namespace qe { | ||||||
|  |     /**
 | ||||||
|  |        Loos-Weispfenning model-based projection for a basic conjunction. | ||||||
|  |        Lits is a vector of literals. | ||||||
|  |        return vector of variables that could not be projected. | ||||||
|  |      */ | ||||||
|  |     expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits); | ||||||
|  | 
 | ||||||
|  |     void arith_project(model& model, app_ref_vector& vars, expr_ref& fml); | ||||||
|  | 
 | ||||||
|  |     void arith_project(model& model, app_ref_vector& vars, expr_ref& fml, expr_map& map); | ||||||
|  | 
 | ||||||
|  |     void array_project_eqs (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars); | ||||||
|  | 
 | ||||||
|  |     void reduce_array_selects (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false); | ||||||
|  | 
 | ||||||
|  |     void reduce_array_selects (model& mdl, expr_ref& fml); | ||||||
|  | 
 | ||||||
|  |     void array_project_selects (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars); | ||||||
|  | 
 | ||||||
|  |     void array_project (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects = false); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										79
									
								
								src/muz/spacer/spacer_smt_context_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/muz/spacer/spacer_smt_context_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_smt_context_manager.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Manager of smt contexts | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2011-11-26. | ||||||
|  |     Arie Gurfinkel | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "spacer_smt_context_manager.h" | ||||||
|  | #include "has_free_vars.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | #include "ast_smt_pp.h" | ||||||
|  | #include <sstream> | ||||||
|  | #include "smt_params.h" | ||||||
|  | 
 | ||||||
|  | #include "ast_pp_util.h" | ||||||
|  | #include "smt_context.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | smt_context_manager::smt_context_manager(ast_manager &m, | ||||||
|  |         unsigned max_num_contexts, | ||||||
|  |         const params_ref &p) : | ||||||
|  |     m_fparams(p), | ||||||
|  |     m(m), | ||||||
|  |     m_max_num_contexts(max_num_contexts), | ||||||
|  |     m_num_contexts(0) { m_stats.reset();} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | smt_context_manager::~smt_context_manager() | ||||||
|  | { | ||||||
|  |     std::for_each(m_solvers.begin(), m_solvers.end(), | ||||||
|  |                   delete_proc<spacer::virtual_solver_factory>()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | virtual_solver* smt_context_manager::mk_fresh() | ||||||
|  | { | ||||||
|  |     ++m_num_contexts; | ||||||
|  |     virtual_solver_factory *solver_factory = 0; | ||||||
|  | 
 | ||||||
|  |     if (m_max_num_contexts == 0 || m_solvers.size() < m_max_num_contexts) { | ||||||
|  |         m_solvers.push_back(alloc(spacer::virtual_solver_factory, m, m_fparams)); | ||||||
|  |         solver_factory = m_solvers.back(); | ||||||
|  |     } else | ||||||
|  |     { solver_factory = m_solvers[(m_num_contexts - 1) % m_max_num_contexts]; } | ||||||
|  | 
 | ||||||
|  |     return solver_factory->mk_solver(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void smt_context_manager::collect_statistics(statistics& st) const | ||||||
|  | { | ||||||
|  |     for (unsigned i = 0; i < m_solvers.size(); ++i) { | ||||||
|  |         m_solvers[i]->collect_statistics(st); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void smt_context_manager::reset_statistics() | ||||||
|  | { | ||||||
|  |     for (unsigned i = 0; i < m_solvers.size(); ++i) { | ||||||
|  |         m_solvers[i]->reset_statistics(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | }; | ||||||
							
								
								
									
										68
									
								
								src/muz/spacer/spacer_smt_context_manager.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/muz/spacer/spacer_smt_context_manager.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_smt_context_manager.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Manager of smt contexts | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Nikolaj Bjorner (nbjorner) 2011-11-26. | ||||||
|  |     Arie Gurfinkel | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_SMT_CONTEXT_MANAGER_H_ | ||||||
|  | #define _SPACER_SMT_CONTEXT_MANAGER_H_ | ||||||
|  | 
 | ||||||
|  | #include "smt_kernel.h" | ||||||
|  | #include "func_decl_dependencies.h" | ||||||
|  | #include "dl_util.h" | ||||||
|  | #include "spacer_virtual_solver.h" | ||||||
|  | #include "stopwatch.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class smt_context_manager { | ||||||
|  | 
 | ||||||
|  |     struct stats { | ||||||
|  |         unsigned m_num_smt_checks; | ||||||
|  |         unsigned m_num_sat_smt_checks; | ||||||
|  |         stats() { reset(); } | ||||||
|  |         void reset() { memset(this, 0, sizeof(*this)); } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     smt_params               m_fparams; | ||||||
|  |     ast_manager&             m; | ||||||
|  |     unsigned                 m_max_num_contexts; | ||||||
|  |     ptr_vector<virtual_solver_factory> m_solvers; | ||||||
|  |     unsigned                 m_num_contexts; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     stats     m_stats; | ||||||
|  |     stopwatch m_check_watch; | ||||||
|  |     stopwatch m_check_sat_watch; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     smt_context_manager(ast_manager& m, unsigned max_num_contexts = 1, | ||||||
|  |                         const params_ref &p = params_ref::get_empty()); | ||||||
|  | 
 | ||||||
|  |     ~smt_context_manager(); | ||||||
|  |     virtual_solver* mk_fresh(); | ||||||
|  | 
 | ||||||
|  |     void collect_statistics(statistics& st) const; | ||||||
|  |     void reset_statistics(); | ||||||
|  | 
 | ||||||
|  |     void updt_params(params_ref const &p) { m_fparams.updt_params(p); } | ||||||
|  |     smt_params& fparams() {return m_fparams;} | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										608
									
								
								src/muz/spacer/spacer_sym_mux.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										608
									
								
								src/muz/spacer/spacer_sym_mux.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,608 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     sym_mux.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     A symbol multiplexer that helps with having multiple versions of each of a set of symbols. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-9-8. | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include <sstream> | ||||||
|  | #include "ast_pp.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include "model.h" | ||||||
|  | #include "rewriter.h" | ||||||
|  | #include "rewriter_def.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "spacer_sym_mux.h" | ||||||
|  | 
 | ||||||
|  | using namespace spacer; | ||||||
|  | 
 | ||||||
|  | sym_mux::sym_mux(ast_manager & m, const std::vector<std::string> & suffixes) | ||||||
|  |     : m(m), m_ref_holder(m), m_next_sym_suffix_idx(0), m_suffixes(suffixes) | ||||||
|  | { | ||||||
|  |     unsigned suf_sz = m_suffixes.size(); | ||||||
|  |     for (unsigned i = 0; i < suf_sz; ++i) { | ||||||
|  |         symbol suff_sym = symbol(m_suffixes[i].c_str()); | ||||||
|  |         m_used_suffixes.insert(suff_sym); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string sym_mux::get_suffix(unsigned i) const | ||||||
|  | { | ||||||
|  |     while (m_suffixes.size() <= i) { | ||||||
|  |         std::string new_suffix; | ||||||
|  |         symbol new_syffix_sym; | ||||||
|  |         do { | ||||||
|  |             std::stringstream stm; | ||||||
|  |             stm << '_' << m_next_sym_suffix_idx; | ||||||
|  |             m_next_sym_suffix_idx++; | ||||||
|  |             new_suffix = stm.str(); | ||||||
|  |             new_syffix_sym = symbol(new_suffix.c_str()); | ||||||
|  |         } while (m_used_suffixes.contains(new_syffix_sym)); | ||||||
|  |         m_used_suffixes.insert(new_syffix_sym); | ||||||
|  |         m_suffixes.push_back(new_suffix); | ||||||
|  |     } | ||||||
|  |     return m_suffixes[i]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, | ||||||
|  |                            unsigned tuple_length, decl_vector & tuple) | ||||||
|  | { | ||||||
|  |     SASSERT(tuple_length > 0); | ||||||
|  |     while (tuple.size() < tuple_length) { | ||||||
|  |         tuple.push_back(0); | ||||||
|  |     } | ||||||
|  |     SASSERT(tuple.size() == tuple_length); | ||||||
|  |     std::string pre = prefix->get_name().str(); | ||||||
|  |     for (unsigned i = 0; i < tuple_length; i++) { | ||||||
|  | 
 | ||||||
|  |         if (tuple[i] != 0) { | ||||||
|  |             SASSERT(tuple[i]->get_arity() == arity); | ||||||
|  |             SASSERT(tuple[i]->get_range() == range); | ||||||
|  |             //domain should match as well, but we won't bother checking an array equality
 | ||||||
|  |         } else { | ||||||
|  |             std::string name = pre + get_suffix(i); | ||||||
|  |             tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range); | ||||||
|  |         } | ||||||
|  |         m_ref_holder.push_back(tuple[i]); | ||||||
|  |         m_sym2idx.insert(tuple[i], i); | ||||||
|  |         m_sym2prim.insert(tuple[i], tuple[0]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     m_prim2all.insert(tuple[0], tuple); | ||||||
|  |     m_prefix2prim.insert(prefix, tuple[0]); | ||||||
|  |     m_prim2prefix.insert(tuple[0], prefix); | ||||||
|  |     m_prim_preds.push_back(tuple[0]); | ||||||
|  |     m_ref_holder.push_back(prefix); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const | ||||||
|  | { | ||||||
|  |     SASSERT(m_prim2all.contains(prim)); | ||||||
|  |     decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value; | ||||||
|  |     SASSERT(tuple[0] == prim); | ||||||
|  | 
 | ||||||
|  |     if (sz <= tuple.size()) { return; } | ||||||
|  | 
 | ||||||
|  |     func_decl * prefix; | ||||||
|  |     TRUSTME(m_prim2prefix.find(prim, prefix)); | ||||||
|  |     std::string prefix_name = prefix->get_name().bare_str(); | ||||||
|  |     for (unsigned i = tuple.size(); i < sz; ++i) { | ||||||
|  |         std::string name = prefix_name + get_suffix(i); | ||||||
|  |         func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), | ||||||
|  |                                              prefix->get_domain(), prefix->get_range()); | ||||||
|  | 
 | ||||||
|  |         tuple.push_back(new_sym); | ||||||
|  |         m_ref_holder.push_back(new_sym); | ||||||
|  |         m_sym2idx.insert(new_sym, i); | ||||||
|  |         m_sym2prim.insert(new_sym, prim); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const | ||||||
|  | { | ||||||
|  |     if (src_idx == tgt_idx) { return sym; } | ||||||
|  |     func_decl * prim = (src_idx == 0) ? sym : get_primary(sym); | ||||||
|  |     if (tgt_idx > src_idx) { | ||||||
|  |         ensure_tuple_size(prim, tgt_idx + 1); | ||||||
|  |     } | ||||||
|  |     decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value; | ||||||
|  |     SASSERT(sym_vect[src_idx] == sym); | ||||||
|  |     return sym_vect[tgt_idx]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, | ||||||
|  |         unsigned arity, sort * const * domain, sort * range) | ||||||
|  | { | ||||||
|  |     func_decl * prim = try_get_primary_by_prefix(prefix); | ||||||
|  |     if (prim) { | ||||||
|  |         SASSERT(prim->get_arity() == arity); | ||||||
|  |         SASSERT(prim->get_range() == range); | ||||||
|  |         //domain should match as well, but we won't bother checking an array equality
 | ||||||
|  | 
 | ||||||
|  |         return conv(prim, 0, idx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     decl_vector syms; | ||||||
|  |     create_tuple(prefix, arity, domain, range, idx + 1, syms); | ||||||
|  |     return syms[idx]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const | ||||||
|  | { | ||||||
|  |     if (!is_app(e)) { return false; } | ||||||
|  |     app * a = to_app(e); | ||||||
|  |     if (m.is_not(a) && is_app(a->get_arg(0))) { | ||||||
|  |         a = to_app(a->get_arg(0)); | ||||||
|  |     } | ||||||
|  |     return is_muxed(a->get_decl()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct sym_mux::formula_checker { | ||||||
|  |     formula_checker(const sym_mux & parent, bool all, unsigned idx) : | ||||||
|  |         m_parent(parent), m_all(all), m_idx(idx), | ||||||
|  |         m_found_what_needed(false) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void operator()(expr * e) | ||||||
|  |     { | ||||||
|  |         if (m_found_what_needed || !is_app(e)) { return; } | ||||||
|  | 
 | ||||||
|  |         func_decl * sym = to_app(e)->get_decl(); | ||||||
|  |         unsigned sym_idx; | ||||||
|  |         if (!m_parent.try_get_index(sym, sym_idx)) { return; } | ||||||
|  | 
 | ||||||
|  |         bool have_idx = sym_idx == m_idx; | ||||||
|  | 
 | ||||||
|  |         if (m_all ? (!have_idx) : have_idx) { | ||||||
|  |             m_found_what_needed = true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool all_have_idx() const | ||||||
|  |     { | ||||||
|  |         SASSERT(m_all); //we were looking for the queried property
 | ||||||
|  |         return !m_found_what_needed; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool some_with_idx() const | ||||||
|  |     { | ||||||
|  |         SASSERT(!m_all); //we were looking for the queried property
 | ||||||
|  |         return m_found_what_needed; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  |     bool m_all; | ||||||
|  |     unsigned m_idx; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     If we check whether all muxed symbols are of given index, we look for | ||||||
|  |     counter-examples, checking whether form contains a muxed symbol of an index, | ||||||
|  |     we look for symbol of index m_idx. | ||||||
|  |     */ | ||||||
|  |     bool m_found_what_needed; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool sym_mux::contains(expr * e, unsigned idx) const | ||||||
|  | { | ||||||
|  |     formula_checker chck(*this, false, idx); | ||||||
|  |     for_each_expr(chck, m_visited, e); | ||||||
|  |     m_visited.reset(); | ||||||
|  |     return chck.some_with_idx(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const | ||||||
|  | { | ||||||
|  |     formula_checker chck(*this, true, idx); | ||||||
|  |     for_each_expr(chck, m_visited, e); | ||||||
|  |     m_visited.reset(); | ||||||
|  |     return chck.all_have_idx(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const | ||||||
|  | { | ||||||
|  |     expr * const * begin = vect.c_ptr(); | ||||||
|  |     expr * const * end = begin + vect.size(); | ||||||
|  |     for (expr * const * it = begin; it != end; it++) { | ||||||
|  |         if (!is_homogenous_formula(*it, idx)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class sym_mux::index_collector { | ||||||
|  |     sym_mux const& m_parent; | ||||||
|  |     svector<bool> m_indices; | ||||||
|  | public: | ||||||
|  |     index_collector(sym_mux const& s): | ||||||
|  |         m_parent(s) {} | ||||||
|  | 
 | ||||||
|  |     void operator()(expr * e) | ||||||
|  |     { | ||||||
|  |         if (is_app(e)) { | ||||||
|  |             func_decl * sym = to_app(e)->get_decl(); | ||||||
|  |             unsigned idx; | ||||||
|  |             if (m_parent.try_get_index(sym, idx)) { | ||||||
|  |                 SASSERT(idx > 0); | ||||||
|  |                 --idx; | ||||||
|  |                 if (m_indices.size() <= idx) { | ||||||
|  |                     m_indices.resize(idx + 1, false); | ||||||
|  |                 } | ||||||
|  |                 m_indices[idx] = true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void extract(unsigned_vector& indices) | ||||||
|  |     { | ||||||
|  |         for (unsigned i = 0; i < m_indices.size(); ++i) { | ||||||
|  |             if (m_indices[i]) { | ||||||
|  |                 indices.push_back(i); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const | ||||||
|  | { | ||||||
|  |     indices.reset(); | ||||||
|  |     index_collector collector(*this); | ||||||
|  |     for_each_expr(collector, m_visited, e); | ||||||
|  |     m_visited.reset(); | ||||||
|  |     collector.extract(indices); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class sym_mux::variable_collector { | ||||||
|  |     sym_mux const& m_parent; | ||||||
|  |     vector<ptr_vector<app> >& m_vars; | ||||||
|  | public: | ||||||
|  |     variable_collector(sym_mux const& s, vector<ptr_vector<app> >& vars): | ||||||
|  |         m_parent(s), m_vars(vars) {} | ||||||
|  | 
 | ||||||
|  |     void operator()(expr * e) | ||||||
|  |     { | ||||||
|  |         if (is_app(e)) { | ||||||
|  |             func_decl * sym = to_app(e)->get_decl(); | ||||||
|  |             unsigned idx; | ||||||
|  |             if (m_parent.try_get_index(sym, idx)) { | ||||||
|  |                 SASSERT(idx > 0); | ||||||
|  |                 --idx; | ||||||
|  |                 if (m_vars.size() <= idx) { | ||||||
|  |                     m_vars.resize(idx + 1, ptr_vector<app>()); | ||||||
|  |                 } | ||||||
|  |                 m_vars[idx].push_back(to_app(e)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void sym_mux::collect_variables(expr* e, vector<ptr_vector<app> >& vars) const | ||||||
|  | { | ||||||
|  |     vars.reset(); | ||||||
|  |     variable_collector collector(*this, vars); | ||||||
|  |     for_each_expr(collector, m_visited, e); | ||||||
|  |     m_visited.reset(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class sym_mux::hmg_checker { | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  | 
 | ||||||
|  |     bool m_found_idx; | ||||||
|  |     unsigned m_idx; | ||||||
|  |     bool m_multiple_indexes; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     hmg_checker(const sym_mux & parent) : | ||||||
|  |         m_parent(parent), m_found_idx(false), m_multiple_indexes(false) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void operator()(expr * e) | ||||||
|  |     { | ||||||
|  |         if (m_multiple_indexes || !is_app(e)) { return; } | ||||||
|  | 
 | ||||||
|  |         func_decl * sym = to_app(e)->get_decl(); | ||||||
|  |         unsigned sym_idx; | ||||||
|  |         if (!m_parent.try_get_index(sym, sym_idx)) { return; } | ||||||
|  | 
 | ||||||
|  |         if (!m_found_idx) { | ||||||
|  |             m_found_idx = true; | ||||||
|  |             m_idx = sym_idx; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         if (m_idx == sym_idx) { return; } | ||||||
|  |         m_multiple_indexes = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool has_multiple_indexes() const | ||||||
|  |     { | ||||||
|  |         return m_multiple_indexes; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool sym_mux::is_homogenous_formula(expr * e) const | ||||||
|  | { | ||||||
|  |     hmg_checker chck(*this); | ||||||
|  |     for_each_expr(chck, m_visited, e); | ||||||
|  |     m_visited.reset(); | ||||||
|  |     return !chck.has_multiple_indexes(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg { | ||||||
|  | private: | ||||||
|  |     ast_manager & m; | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  |     unsigned m_from_idx; | ||||||
|  |     unsigned m_to_idx; | ||||||
|  |     bool m_homogenous; | ||||||
|  | public: | ||||||
|  |     conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) | ||||||
|  |         : m(parent.get_manager()), | ||||||
|  |           m_parent(parent), | ||||||
|  |           m_from_idx(from_idx), | ||||||
|  |           m_to_idx(to_idx), | ||||||
|  |           m_homogenous(homogenous) {} | ||||||
|  | 
 | ||||||
|  |     bool get_subst(expr * s, expr * & t, proof * & t_pr) | ||||||
|  |     { | ||||||
|  |         if (!is_app(s)) { return false; } | ||||||
|  |         app * a = to_app(s); | ||||||
|  |         func_decl * sym = a->get_decl(); | ||||||
|  |         if (!m_parent.has_index(sym, m_from_idx)) { | ||||||
|  |             SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx); | ||||||
|  | 
 | ||||||
|  |         t = m.mk_app(tgt, a->get_args()); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const | ||||||
|  | { | ||||||
|  |     if (src_idx == tgt_idx) { | ||||||
|  |         res = f; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); | ||||||
|  |     rewriter_tpl<conv_rewriter_cfg> rwr(m, false, r_cfg); | ||||||
|  |     rwr(f, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg { | ||||||
|  | private: | ||||||
|  |     ast_manager & m; | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  |     int m_shift; | ||||||
|  | public: | ||||||
|  |     shifting_rewriter_cfg(const sym_mux & parent, int shift) | ||||||
|  |         : m(parent.get_manager()), | ||||||
|  |           m_parent(parent), | ||||||
|  |           m_shift(shift) {} | ||||||
|  | 
 | ||||||
|  |     bool get_subst(expr * s, expr * & t, proof * & t_pr) | ||||||
|  |     { | ||||||
|  |         if (!is_app(s)) { return false; } | ||||||
|  |         app * a = to_app(s); | ||||||
|  |         func_decl * sym = a->get_decl(); | ||||||
|  | 
 | ||||||
|  |         unsigned idx; | ||||||
|  |         if (!m_parent.try_get_index(sym, idx)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         SASSERT(static_cast<int>(idx) + m_shift >= 0); | ||||||
|  |         func_decl * tgt = m_parent.conv(sym, idx, idx + m_shift); | ||||||
|  |         t = m.mk_app(tgt, a->get_args()); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) const | ||||||
|  | { | ||||||
|  |     if (dist == 0) { | ||||||
|  |         res = f; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     shifting_rewriter_cfg r_cfg(*this, dist); | ||||||
|  |     rewriter_tpl<shifting_rewriter_cfg> rwr(m, false, r_cfg); | ||||||
|  |     rwr(f, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, | ||||||
|  |                                   expr_ref_vector & res) const | ||||||
|  | { | ||||||
|  |     res.reset(); | ||||||
|  |     expr * const * begin = vect.c_ptr(); | ||||||
|  |     expr * const * end = begin + vect.size(); | ||||||
|  |     for (expr * const * it = begin; it != end; it++) { | ||||||
|  |         expr_ref converted(m); | ||||||
|  |         conv_formula(*it, src_idx, tgt_idx, converted); | ||||||
|  |         res.push_back(converted); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const | ||||||
|  | { | ||||||
|  |     unsigned i = 0; | ||||||
|  |     while (i < vect.size()) { | ||||||
|  |         expr* e = vect[i].get(); | ||||||
|  |         if (contains(e, idx) && is_homogenous_formula(e, idx)) { | ||||||
|  |             i++; | ||||||
|  |         } else { | ||||||
|  |             //we don't allow mixing states inside vector elements
 | ||||||
|  |             SASSERT(!contains(e, idx)); | ||||||
|  |             vect[i] = vect.back(); | ||||||
|  |             vect.pop_back(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sym_mux::partition_o_idx( | ||||||
|  |     expr_ref_vector const& lits, | ||||||
|  |     expr_ref_vector& o_lits, | ||||||
|  |     expr_ref_vector& other, unsigned idx) const | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0; i < lits.size(); ++i) { | ||||||
|  |         if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) { | ||||||
|  |             o_lits.push_back(lits[i]); | ||||||
|  |         } else { | ||||||
|  |             other.push_back(lits[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class sym_mux::nonmodel_sym_checker { | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  | 
 | ||||||
|  |     bool m_found; | ||||||
|  | public: | ||||||
|  |     nonmodel_sym_checker(const sym_mux & parent) : | ||||||
|  |         m_parent(parent), m_found(false) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void operator()(expr * e) | ||||||
|  |     { | ||||||
|  |         if (m_found || !is_app(e)) { return; } | ||||||
|  | 
 | ||||||
|  |         func_decl * sym = to_app(e)->get_decl(); | ||||||
|  | 
 | ||||||
|  |         if (m_parent.is_non_model_sym(sym)) { | ||||||
|  |             m_found = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool found() const | ||||||
|  |     { | ||||||
|  |         return m_found; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool sym_mux::has_nonmodel_symbol(expr * e) const | ||||||
|  | { | ||||||
|  |     nonmodel_sym_checker chck(*this); | ||||||
|  |     for_each_expr(chck, e); | ||||||
|  |     return chck.found(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const | ||||||
|  | { | ||||||
|  |     unsigned i = 0; | ||||||
|  |     while (i < vect.size()) { | ||||||
|  |         if (!has_nonmodel_symbol(vect[i].get())) { | ||||||
|  |             i++; | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         vect[i] = vect.back(); | ||||||
|  |         vect.pop_back(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class sym_mux::decl_idx_comparator { | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  | public: | ||||||
|  |     decl_idx_comparator(const sym_mux & parent) | ||||||
|  |         : m_parent(parent) | ||||||
|  |     { } | ||||||
|  | 
 | ||||||
|  |     bool operator()(func_decl * sym1, func_decl * sym2) | ||||||
|  |     { | ||||||
|  |         unsigned idx1, idx2; | ||||||
|  |         if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; } | ||||||
|  |         if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; } | ||||||
|  | 
 | ||||||
|  |         if (idx1 != idx2) { return idx1 < idx2; } | ||||||
|  |         return lt(sym1->get_name(), sym2->get_name()); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | std::string sym_mux::pp_model(const model_core & mdl) const | ||||||
|  | { | ||||||
|  |     decl_vector consts; | ||||||
|  |     unsigned sz = mdl.get_num_constants(); | ||||||
|  |     for (unsigned i = 0; i < sz; i++) { | ||||||
|  |         func_decl * d = mdl.get_constant(i); | ||||||
|  |         consts.push_back(d); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this)); | ||||||
|  | 
 | ||||||
|  |     std::stringstream res; | ||||||
|  | 
 | ||||||
|  |     decl_vector::iterator end = consts.end(); | ||||||
|  |     for (decl_vector::iterator it = consts.begin(); it != end; it++) { | ||||||
|  |         func_decl * d = *it; | ||||||
|  |         std::string name   = d->get_name().str(); | ||||||
|  |         const char * arrow = " -> "; | ||||||
|  |         res << name << arrow; | ||||||
|  |         unsigned indent = static_cast<unsigned>(name.length() + strlen(arrow)); | ||||||
|  |         res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n"; | ||||||
|  | 
 | ||||||
|  |         if (it + 1 != end) { | ||||||
|  |             unsigned idx1, idx2; | ||||||
|  |             if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; } | ||||||
|  |             if (!try_get_index(*(it + 1), idx2)) { idx2 = UINT_MAX; } | ||||||
|  |             if (idx1 != idx2) { res << "\n"; } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return res.str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #if 0 | ||||||
|  | 
 | ||||||
|  | class sym_mux::index_renamer_cfg : public default_rewriter_cfg { | ||||||
|  |     const sym_mux & m_parent; | ||||||
|  |     unsigned        m_idx; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {} | ||||||
|  | 
 | ||||||
|  |     bool get_subst(expr * s, expr * & t, proof * & t_pr) | ||||||
|  |     { | ||||||
|  |         if (!is_app(s)) { return false; } | ||||||
|  |         app * a = to_app(s); | ||||||
|  |         if (a->get_family_id() != null_family_id) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         func_decl * sym = a->get_decl(); | ||||||
|  |         unsigned idx; | ||||||
|  |         if (!m_parent.try_get_index(sym, idx)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         if (m_idx == idx) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         ast_manager& m = m_parent.get_manager(); | ||||||
|  |         symbol name = symbol((sym->get_name().str() + "!").c_str()); | ||||||
|  |         func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range()); | ||||||
|  |         t = m.mk_app(tgt, a->get_num_args(), a->get_args()); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										256
									
								
								src/muz/spacer/spacer_sym_mux.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								src/muz/spacer/spacer_sym_mux.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,256 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     sym_mux.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     A symbol multiplexer that helps with having multiple versions of each of a set of symbols. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-9-8. | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SYM_MUX_H_ | ||||||
|  | #define _SYM_MUX_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | #include "map.h" | ||||||
|  | #include "vector.h" | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | class model_core; | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | class sym_mux { | ||||||
|  | public: | ||||||
|  |     typedef ptr_vector<app> app_vector; | ||||||
|  |     typedef ptr_vector<func_decl> decl_vector; | ||||||
|  | private: | ||||||
|  |     typedef obj_map<func_decl, unsigned> sym2u; | ||||||
|  |     typedef obj_map<func_decl, decl_vector> sym2dv; | ||||||
|  |     typedef obj_map<func_decl, func_decl *> sym2sym; | ||||||
|  |     typedef obj_map<func_decl, func_decl *> sym2pred; | ||||||
|  |     typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbols; | ||||||
|  | 
 | ||||||
|  |     ast_manager &           m; | ||||||
|  |     mutable ast_ref_vector  m_ref_holder; | ||||||
|  |     mutable expr_mark       m_visited; | ||||||
|  | 
 | ||||||
|  |     mutable unsigned       m_next_sym_suffix_idx; | ||||||
|  |     mutable symbols        m_used_suffixes; | ||||||
|  |     /** Here we have default suffixes for each of the variants */ | ||||||
|  |     mutable std::vector<std::string> m_suffixes; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Primary symbol is the 0-th variant. This member maps from primary symbol | ||||||
|  |        to vector of all its variants (including the primary variant). | ||||||
|  |     */ | ||||||
|  |     sym2dv m_prim2all; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        For each symbol contains its variant index | ||||||
|  |     */ | ||||||
|  |     mutable sym2u m_sym2idx; | ||||||
|  |     /**
 | ||||||
|  |        For each symbol contains its primary variant | ||||||
|  |     */ | ||||||
|  |     mutable sym2sym m_sym2prim; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Maps prefixes passed to the create_tuple to | ||||||
|  |        the primary symbol created from it. | ||||||
|  |     */ | ||||||
|  |     sym2pred m_prefix2prim; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |        Maps pripary symbols to prefixes that were used to create them. | ||||||
|  |     */ | ||||||
|  |     sym2sym     m_prim2prefix; | ||||||
|  | 
 | ||||||
|  |     decl_vector m_prim_preds; | ||||||
|  | 
 | ||||||
|  |     obj_hashtable<func_decl> m_non_model_syms; | ||||||
|  | 
 | ||||||
|  |     struct formula_checker; | ||||||
|  |     struct conv_rewriter_cfg; | ||||||
|  |     struct shifting_rewriter_cfg; | ||||||
|  |     class decl_idx_comparator; | ||||||
|  |     class hmg_checker; | ||||||
|  |     class nonmodel_sym_checker; | ||||||
|  |     class index_renamer_cfg; | ||||||
|  |     class index_collector; | ||||||
|  |     class variable_collector; | ||||||
|  | 
 | ||||||
|  |     std::string get_suffix(unsigned i) const; | ||||||
|  |     void ensure_tuple_size(func_decl * prim, unsigned sz) const; | ||||||
|  | 
 | ||||||
|  |     expr_ref isolate_o_idx(expr* e, unsigned idx) const; | ||||||
|  | public: | ||||||
|  |     sym_mux(ast_manager & m, const std::vector<std::string> & suffixes); | ||||||
|  | 
 | ||||||
|  |     ast_manager & get_manager() const { return m; } | ||||||
|  | 
 | ||||||
|  |     bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } | ||||||
|  | 
 | ||||||
|  |     bool try_get_index(func_decl * sym, unsigned & idx) const | ||||||
|  |     { | ||||||
|  |         return m_sym2idx.find(sym, idx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool has_index(func_decl * sym, unsigned idx) const | ||||||
|  |     { | ||||||
|  |         unsigned actual_idx; | ||||||
|  |         return try_get_index(sym, actual_idx) && idx == actual_idx; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** Return primary symbol. sym must be muxed. */ | ||||||
|  |     func_decl * get_primary(func_decl * sym) const | ||||||
|  |     { | ||||||
|  |         func_decl * prim; | ||||||
|  |         TRUSTME(m_sym2prim.find(sym, prim)); | ||||||
|  |         return prim; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Return primary symbol created from prefix, or 0 if the prefix was never used. | ||||||
|  |     */ | ||||||
|  |     func_decl * try_get_primary_by_prefix(func_decl* prefix) const | ||||||
|  |     { | ||||||
|  |         func_decl * res; | ||||||
|  |         if(!m_prefix2prim.find(prefix, res)) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         return res; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Return symbol created from prefix, or 0 if the prefix was never used. | ||||||
|  |     */ | ||||||
|  |     func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) const | ||||||
|  |     { | ||||||
|  |         func_decl * prim = try_get_primary_by_prefix(prefix); | ||||||
|  |         if(!prim) { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |         return conv(prim, 0, idx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Marks symbol as non-model which means it will not appear in models collected by | ||||||
|  |     get_muxed_cube_from_model function. | ||||||
|  |     This is to take care of auxiliary symbols introduced by the disjunction relations | ||||||
|  |     to relativize lemmas coming from disjuncts. | ||||||
|  |     */ | ||||||
|  |     void mark_as_non_model(func_decl * sym) | ||||||
|  |     { | ||||||
|  |         SASSERT(is_muxed(sym)); | ||||||
|  |         m_non_model_syms.insert(get_primary(sym)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, | ||||||
|  |             unsigned arity, sort * const * domain, sort * range); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     bool is_muxed_lit(expr * e, unsigned idx) const; | ||||||
|  | 
 | ||||||
|  |     bool is_non_model_sym(func_decl * s) const | ||||||
|  |     { | ||||||
|  |         return is_muxed(s) && m_non_model_syms.contains(get_primary(s)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Create a multiplexed tuple of propositional constants. | ||||||
|  |     Symbols may be suplied in the tuple vector, | ||||||
|  |     those beyond the size of the array and those with corresponding positions | ||||||
|  |     assigned to zero will be created using prefix. | ||||||
|  |     Tuple length must be at least one. | ||||||
|  |     */ | ||||||
|  |     void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, | ||||||
|  |                       unsigned tuple_length, decl_vector & tuple); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Return true if the only multiplexed symbols which e contains are of index idx. | ||||||
|  |     */ | ||||||
|  |     bool is_homogenous_formula(expr * e, unsigned idx) const; | ||||||
|  |     bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Return true if all multiplexed symbols which e contains are of one index. | ||||||
|  |     */ | ||||||
|  |     bool is_homogenous_formula(expr * e) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Return true if expression e contains a muxed symbol of index idx. | ||||||
|  |     */ | ||||||
|  |     bool contains(expr * e, unsigned idx) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         Collect indices used in expression. | ||||||
|  |     */ | ||||||
|  |     void collect_indices(expr* e, unsigned_vector& indices) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         Collect used variables of each index. | ||||||
|  |     */ | ||||||
|  |     void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Convert symbol sym which has to be of src_idx variant into variant tgt_idx. | ||||||
|  |     */ | ||||||
|  |     func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Convert src_idx symbols in formula f variant into tgt_idx. | ||||||
|  |     If homogenous is true, formula cannot contain symbols of other variants. | ||||||
|  |     */ | ||||||
|  |     void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous = true) const; | ||||||
|  |     void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, | ||||||
|  |                              expr_ref_vector & res) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift | ||||||
|  |     symbol index to a negative value. | ||||||
|  |     */ | ||||||
|  |     void shift_formula(expr * f, int dist, expr_ref & res) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |     Remove from vect literals (atoms or negations of atoms) of symbols | ||||||
|  |     that contain multiplexed symbols with indexes other than idx. | ||||||
|  | 
 | ||||||
|  |     Each of the literals can contain only symbols multiplexed with one index | ||||||
|  |     (this trivially holds if the literals are propositional). | ||||||
|  | 
 | ||||||
|  |     Order of elements in vect may be modified by this function | ||||||
|  |     */ | ||||||
|  |     void filter_idx(expr_ref_vector & vect, unsigned idx) const; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |         Partition literals into o_literals and others. | ||||||
|  |     */ | ||||||
|  |     void partition_o_idx(expr_ref_vector const& lits, | ||||||
|  |                          expr_ref_vector& o_lits, | ||||||
|  |                          expr_ref_vector& other, unsigned idx) const; | ||||||
|  | 
 | ||||||
|  |     bool has_nonmodel_symbol(expr * e) const; | ||||||
|  |     void filter_non_model_lits(expr_ref_vector & vect) const; | ||||||
|  | 
 | ||||||
|  |     func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); } | ||||||
|  |     func_decl * const * end_prim_preds() const { return m_prim_preds.end(); } | ||||||
|  | 
 | ||||||
|  |     void get_muxed_cube_from_model(const model_core & model, expr_ref_vector & res) const; | ||||||
|  | 
 | ||||||
|  |     std::string pp_model(const model_core & mdl) const; | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										360
									
								
								src/muz/spacer/spacer_unsat_core_learner.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										360
									
								
								src/muz/spacer/spacer_unsat_core_learner.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,360 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_unsat_core_learner.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |    itp cores | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include "spacer_unsat_core_learner.h" | ||||||
|  | 
 | ||||||
|  | #include "spacer_unsat_core_plugin.h" | ||||||
|  | 
 | ||||||
|  | #include "proof_utils.h" | ||||||
|  | #include "for_each_expr.h" | ||||||
|  | #include <unordered_map> | ||||||
|  | namespace spacer | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | #pragma mark - proof iterators | ||||||
|  | 
 | ||||||
|  | # pragma mark - main methods | ||||||
|  | unsat_core_learner::~unsat_core_learner() | ||||||
|  | { | ||||||
|  |     std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc<unsat_core_plugin>()); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) | ||||||
|  | { | ||||||
|  |     m_plugins.push_back(plugin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, expr_ref_vector& unsat_core) | ||||||
|  | { | ||||||
|  |     // transform proof in order to get a proof which is better suited for unsat-core-extraction
 | ||||||
|  |     proof_ref pr(root, m); | ||||||
|  | 
 | ||||||
|  |     spacer::reduce_hypotheses(pr); | ||||||
|  |     STRACE("spacer.unsat_core_learner", | ||||||
|  |            verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     // compute symbols occuring in B
 | ||||||
|  |     collect_symbols_b(asserted_b); | ||||||
|  | 
 | ||||||
|  |     // traverse proof
 | ||||||
|  |     ProofIteratorPostOrder it(root, m); | ||||||
|  |     while (it.hasNext()) | ||||||
|  |     { | ||||||
|  |         proof* currentNode = it.next(); | ||||||
|  | 
 | ||||||
|  |         if (m.get_num_parents(currentNode) == 0) | ||||||
|  |         { | ||||||
|  |             switch(currentNode->get_decl_kind()) | ||||||
|  |             { | ||||||
|  | 
 | ||||||
|  |                 case PR_ASSERTED: // currentNode is an axiom
 | ||||||
|  |                 { | ||||||
|  |                     if (asserted_b.contains(m.get_fact(currentNode))) | ||||||
|  |                     { | ||||||
|  |                         m_b_mark.mark(currentNode, true); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         m_a_mark.mark(currentNode, true); | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                     // currentNode is a hypothesis:
 | ||||||
|  |                 case PR_HYPOTHESIS: | ||||||
|  |                 { | ||||||
|  |                     m_h_mark.mark(currentNode, true); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 default: | ||||||
|  |                 { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             // collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis
 | ||||||
|  |             bool need_to_mark_a = false; | ||||||
|  |             bool need_to_mark_b = false; | ||||||
|  |             bool need_to_mark_h = false; | ||||||
|  |             bool need_to_mark_closed = true; | ||||||
|  | 
 | ||||||
|  |             for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) | ||||||
|  |             { | ||||||
|  |                 SASSERT(m.is_proof(currentNode->get_arg(i))); | ||||||
|  |                 proof* premise = to_app(currentNode->get_arg(i)); | ||||||
|  | 
 | ||||||
|  |                 need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise); | ||||||
|  |                 need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise); | ||||||
|  |                 need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise); | ||||||
|  |                 need_to_mark_closed = need_to_mark_closed && (!m_b_mark.is_marked(premise) || m_closed.is_marked(premise)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // if current node is application of lemma, we know that all hypothesis are removed
 | ||||||
|  |             if(currentNode->get_decl_kind() == PR_LEMMA) | ||||||
|  |             { | ||||||
|  |                 need_to_mark_h = false; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // save results
 | ||||||
|  |             m_a_mark.mark(currentNode, need_to_mark_a); | ||||||
|  |             m_b_mark.mark(currentNode, need_to_mark_b); | ||||||
|  |             m_h_mark.mark(currentNode, need_to_mark_h); | ||||||
|  |             m_closed.mark(currentNode, need_to_mark_closed); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // we have now collected all necessary information, so we can visit the node
 | ||||||
|  |         // if the node mixes A-reasoning and B-reasoning and contains non-closed premises
 | ||||||
|  |         if (m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode) && !m_closed.is_marked(currentNode)) | ||||||
|  |         { | ||||||
|  |             compute_partial_core(currentNode); // then we need to compute a partial core
 | ||||||
|  |             // SASSERT(!(m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode)) || m_closed.is_marked(currentNode)); TODO: doesn't hold anymore if we do the mincut-thing!
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // give plugins chance to finalize their unsat-core-computation
 | ||||||
|  |     finalize(); | ||||||
|  | 
 | ||||||
|  |     // TODO: remove duplicates from unsat core?
 | ||||||
|  | 
 | ||||||
|  |     bool debug_proof = false; | ||||||
|  |     if(debug_proof) | ||||||
|  |     { | ||||||
|  |         // print proof for debugging
 | ||||||
|  |         verbose_stream() << "\n\nProof:\n"; | ||||||
|  |         std::unordered_map<unsigned, unsigned> id_to_small_id; | ||||||
|  |         unsigned counter = 0; | ||||||
|  | 
 | ||||||
|  |         ProofIteratorPostOrder it2(root, m); | ||||||
|  |         while (it2.hasNext()) | ||||||
|  |         { | ||||||
|  |             proof* currentNode = it2.next(); | ||||||
|  | 
 | ||||||
|  |             SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end()); | ||||||
|  |             id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter)); | ||||||
|  | 
 | ||||||
|  |             verbose_stream() << counter << " "; | ||||||
|  |             verbose_stream() << "["; | ||||||
|  |             if (is_a_marked(currentNode)) | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << "a"; | ||||||
|  |             } | ||||||
|  |             if (is_b_marked(currentNode)) | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << "b"; | ||||||
|  |             } | ||||||
|  |             if (is_h_marked(currentNode)) | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << "h"; | ||||||
|  |             } | ||||||
|  |             if (is_closed(currentNode)) | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << "c"; | ||||||
|  |             } | ||||||
|  |             verbose_stream() << "] "; | ||||||
|  | 
 | ||||||
|  |             if (m.get_num_parents(currentNode) == 0) | ||||||
|  |             { | ||||||
|  |                 switch (currentNode->get_decl_kind()) | ||||||
|  |                 { | ||||||
|  |                     case PR_ASSERTED: | ||||||
|  |                         verbose_stream() << "asserted"; | ||||||
|  |                         break; | ||||||
|  |                     case PR_HYPOTHESIS: | ||||||
|  |                         verbose_stream() << "hypothesis"; | ||||||
|  |                         break; | ||||||
|  |                     default: | ||||||
|  |                         verbose_stream() << "unknown axiom-type"; | ||||||
|  |                         break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (currentNode->get_decl_kind() == PR_LEMMA) | ||||||
|  |                 { | ||||||
|  |                     verbose_stream() << "lemma"; | ||||||
|  |                 } | ||||||
|  |                 else if (currentNode->get_decl_kind() == PR_TH_LEMMA) | ||||||
|  |                 { | ||||||
|  |                     verbose_stream() << "th_lemma"; | ||||||
|  |                     func_decl* d = currentNode->get_decl(); | ||||||
|  |                     symbol sym; | ||||||
|  |                     if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step
 | ||||||
|  |                         d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas",
 | ||||||
|  |                         d->get_parameter(1).is_symbol(sym) && sym == "farkas") | ||||||
|  |                     { | ||||||
|  |                         verbose_stream() << "(farkas)"; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         verbose_stream() << "(other)"; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     verbose_stream() << "step"; | ||||||
|  |                 } | ||||||
|  |                 verbose_stream() << " from "; | ||||||
|  |                 for (int i = m.get_num_parents(currentNode) - 1; i >= 0  ; --i) | ||||||
|  |                 { | ||||||
|  |                     proof* premise = to_app(currentNode->get_arg(i)); | ||||||
|  |                     unsigned premise_small_id = id_to_small_id[premise->get_id()]; | ||||||
|  |                     if (i > 0) | ||||||
|  |                     { | ||||||
|  |                         verbose_stream() << premise_small_id << ", "; | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         verbose_stream() << premise_small_id; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (currentNode->get_decl_kind() == PR_TH_LEMMA || (is_a_marked(currentNode) && is_b_marked(currentNode)) || is_h_marked(currentNode) || (!is_a_marked(currentNode) && !is_b_marked(currentNode))) | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << std::endl; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 verbose_stream() << ": " << mk_pp(m.get_fact(currentNode), m) << std::endl; | ||||||
|  |             } | ||||||
|  |             ++counter; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // move all lemmas into vector
 | ||||||
|  |     for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) | ||||||
|  |     { | ||||||
|  |         unsat_core.push_back(*it); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_learner::compute_partial_core(proof* step) | ||||||
|  | { | ||||||
|  |     for (unsat_core_plugin** it=m_plugins.begin(), **end = m_plugins.end (); it != end && !m_closed.is_marked(step); ++it) | ||||||
|  |     { | ||||||
|  |         unsat_core_plugin* plugin = *it; | ||||||
|  |         plugin->compute_partial_core(step); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_learner::finalize() | ||||||
|  | { | ||||||
|  |     for (unsat_core_plugin** it=m_plugins.begin(); it != m_plugins.end(); ++it) | ||||||
|  |     { | ||||||
|  |         unsat_core_plugin* plugin = *it; | ||||||
|  |         plugin->finalize(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #pragma mark - API | ||||||
|  | 
 | ||||||
|  | bool unsat_core_learner::is_a_marked(proof* p) | ||||||
|  | { | ||||||
|  |     return m_a_mark.is_marked(p); | ||||||
|  | } | ||||||
|  | bool unsat_core_learner::is_b_marked(proof* p) | ||||||
|  | { | ||||||
|  |     return m_b_mark.is_marked(p); | ||||||
|  | } | ||||||
|  | bool unsat_core_learner::is_h_marked(proof* p) | ||||||
|  | { | ||||||
|  |     return m_h_mark.is_marked(p); | ||||||
|  | } | ||||||
|  | bool unsat_core_learner::is_closed(proof*p) | ||||||
|  | { | ||||||
|  |     return m_closed.is_marked(p); | ||||||
|  | } | ||||||
|  | void unsat_core_learner::set_closed(proof* p, bool value) | ||||||
|  | { | ||||||
|  |     m_closed.mark(p, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |     void unsat_core_learner::add_lemma_to_core(expr* lemma) | ||||||
|  |     { | ||||||
|  |         m_unsat_core.push_back(lemma); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | # pragma mark - checking for b_symbols | ||||||
|  | 
 | ||||||
|  | class collect_pure_proc { | ||||||
|  |     func_decl_set& m_symbs; | ||||||
|  | public: | ||||||
|  |     collect_pure_proc(func_decl_set& s):m_symbs(s) {} | ||||||
|  | 
 | ||||||
|  |     void operator()(app* a) { | ||||||
|  |         if (a->get_family_id() == null_family_id) { | ||||||
|  |             m_symbs.insert(a->get_decl()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator()(var*) {} | ||||||
|  |     void operator()(quantifier*) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void unsat_core_learner::collect_symbols_b(expr_set axioms_b) | ||||||
|  | { | ||||||
|  |     expr_mark visited; | ||||||
|  |     collect_pure_proc proc(m_symbols_b); | ||||||
|  |     for (expr_set::iterator it = axioms_b.begin(); it != axioms_b.end(); ++it) | ||||||
|  |     { | ||||||
|  |         for_each_expr(proc, visited, *it); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class is_pure_expr_proc { | ||||||
|  |     func_decl_set const& m_symbs; | ||||||
|  |     array_util           m_au; | ||||||
|  | public: | ||||||
|  |     struct non_pure {}; | ||||||
|  | 
 | ||||||
|  |     is_pure_expr_proc(func_decl_set const& s, ast_manager& m): | ||||||
|  |     m_symbs(s), | ||||||
|  |     m_au (m) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     void operator()(app* a) { | ||||||
|  |         if (a->get_family_id() == null_family_id) { | ||||||
|  |             if (!m_symbs.contains(a->get_decl())) { | ||||||
|  |                 throw non_pure(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else if (a->get_family_id () == m_au.get_family_id () && | ||||||
|  |                  a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { | ||||||
|  |             throw non_pure(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     void operator()(var*) {} | ||||||
|  |     void operator()(quantifier*) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool unsat_core_learner::only_contains_symbols_b(expr* expr) const | ||||||
|  | { | ||||||
|  |     is_pure_expr_proc proc(m_symbols_b, m); | ||||||
|  |     try { | ||||||
|  |         for_each_expr(proc, expr); | ||||||
|  |     } | ||||||
|  |     catch (is_pure_expr_proc::non_pure) | ||||||
|  |     { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										107
									
								
								src/muz/spacer/spacer_unsat_core_learner.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/muz/spacer/spacer_unsat_core_learner.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_unsat_core_learner.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |    itp cores | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef _SPACER_UNSAT_CORE_LEARNER_H_ | ||||||
|  | #define _SPACER_UNSAT_CORE_LEARNER_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "spacer_proof_utils.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     class unsat_core_plugin; | ||||||
|  |     class unsat_core_learner | ||||||
|  |     { | ||||||
|  |         typedef obj_hashtable<expr> expr_set; | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         unsat_core_learner(ast_manager& m) : m(m), m_unsat_core(m) {}; | ||||||
|  |         virtual ~unsat_core_learner(); | ||||||
|  | 
 | ||||||
|  |         ast_manager& m; | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * register a plugin for computation of partial unsat cores | ||||||
|  |          * currently plugins are called in the order they have been registered | ||||||
|  |          */ | ||||||
|  |         void register_plugin(unsat_core_plugin* plugin); | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * compute unsat core using the registered unsat-core-plugins | ||||||
|  |          */ | ||||||
|  |         void compute_unsat_core(proof* root, expr_set& asserted_b, expr_ref_vector& unsat_core); | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * getter/setter methods for data structures exposed to plugins | ||||||
|  |          * the following invariants can be assumed and need to be maintained by the plugins: | ||||||
|  |          *  - a node is a-marked iff it is derived using at least one asserted proof step from A. | ||||||
|  |          *  - a node is b-marked iff its derivation contains no asserted proof steps from A and | ||||||
|  |          *    no hypothesis (with the additional complication that lemmas conceptually remove hypothesis) | ||||||
|  |          *  - a node is h-marked, iff it is derived using at least one hypothesis | ||||||
|  |          *  - a node is closed, iff it has already been interpolated, i.e. its contribution is | ||||||
|  |          *    already covered by the unsat-core. | ||||||
|  |          */ | ||||||
|  |         bool is_a_marked(proof* p); | ||||||
|  |         bool is_b_marked(proof* p); | ||||||
|  |         bool is_h_marked(proof* p); | ||||||
|  |         bool is_closed(proof* p); | ||||||
|  |         void set_closed(proof* p, bool value); | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * adds a lemma to the unsat core | ||||||
|  |          */ | ||||||
|  |         void add_lemma_to_core(expr* lemma); | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * helper method, which can be used by plugins | ||||||
|  |          * returns true iff all symbols of expr occur in some b-asserted formula. | ||||||
|  |          * must only be called after a call to collect_symbols_b. | ||||||
|  |          */ | ||||||
|  |         bool only_contains_symbols_b(expr* expr) const; | ||||||
|  |         bool is_b_pure (proof *p) | ||||||
|  |         {return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));} | ||||||
|  |         bool is_b_open (proof *p) | ||||||
|  |         { return is_b_marked (p) && !is_closed (p); } | ||||||
|  | 
 | ||||||
|  |     private: | ||||||
|  |         ptr_vector<unsat_core_plugin> m_plugins; | ||||||
|  |         func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula
 | ||||||
|  |         void collect_symbols_b(expr_set axioms_b); | ||||||
|  | 
 | ||||||
|  |         ast_mark m_a_mark; | ||||||
|  |         ast_mark m_b_mark; | ||||||
|  |         ast_mark m_h_mark; | ||||||
|  |         ast_mark m_closed; | ||||||
|  | 
 | ||||||
|  |         expr_ref_vector m_unsat_core; // collects the lemmas of the unsat-core, will at the end be inserted into unsat_core.
 | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * computes partial core for step by delegating computation to plugins | ||||||
|  |          */ | ||||||
|  |         void compute_partial_core(proof* step); | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |          * finalize computation of unsat-core | ||||||
|  |          */ | ||||||
|  |         void finalize(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										776
									
								
								src/muz/spacer/spacer_unsat_core_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										776
									
								
								src/muz/spacer/spacer_unsat_core_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,776 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_unsat_core_plugin.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |    plugin for itp cores | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #include "spacer_unsat_core_plugin.h" | ||||||
|  | 
 | ||||||
|  | #include "spacer_unsat_core_learner.h" | ||||||
|  | 
 | ||||||
|  | #include "smt_farkas_util.h" | ||||||
|  | #include "bool_rewriter.h" | ||||||
|  | #include "arith_decl_plugin.h" | ||||||
|  | #include <set> | ||||||
|  | #include "smt_solver.h" | ||||||
|  | #include "solver.h" | ||||||
|  | #include <limits> | ||||||
|  | #include "spacer_proof_utils.h" | ||||||
|  | #include "spacer_matrix.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | #pragma mark - unsat_core_plugin_lemma | ||||||
|  | 
 | ||||||
|  | void unsat_core_plugin_lemma::compute_partial_core(proof* step) | ||||||
|  | { | ||||||
|  |     SASSERT(m_learner.is_a_marked(step)); | ||||||
|  |     SASSERT(m_learner.is_b_marked(step)); | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) | ||||||
|  |     { | ||||||
|  |         SASSERT(m_learner.m.is_proof(step->get_arg(i))); | ||||||
|  |         proof* premise = to_app(step->get_arg(i)); | ||||||
|  | 
 | ||||||
|  |         if (m_learner.is_b_open (premise)) | ||||||
|  |         { | ||||||
|  |             // by IH, premises that are AB marked are already closed
 | ||||||
|  |             SASSERT(!m_learner.is_a_marked(premise)); | ||||||
|  |             add_lowest_split_to_core(premise); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     m_learner.set_closed(step, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const | ||||||
|  | { | ||||||
|  |     SASSERT(m_learner.is_b_open(step)); | ||||||
|  |     ast_manager &m = m_learner.m; | ||||||
|  | 
 | ||||||
|  |     ptr_vector<proof> todo; | ||||||
|  |     todo.push_back(step); | ||||||
|  | 
 | ||||||
|  |     while (!todo.empty()) | ||||||
|  |     { | ||||||
|  |         proof* pf = todo.back(); | ||||||
|  |         todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |         // if current step hasn't been processed,
 | ||||||
|  |         if (!m_learner.is_closed(pf)) | ||||||
|  |         { | ||||||
|  |             m_learner.set_closed(pf, true); | ||||||
|  |             // the step is b-marked and not closed.
 | ||||||
|  |             // by I.H. the step must be already visited
 | ||||||
|  |             // so if it is also a-marked, it must be closed
 | ||||||
|  |             SASSERT(m_learner.is_b_marked(pf)); | ||||||
|  |             SASSERT(!m_learner.is_a_marked(pf)); | ||||||
|  | 
 | ||||||
|  |             // the current step needs to be interpolated:
 | ||||||
|  |             expr* fact = m_learner.m.get_fact(pf); | ||||||
|  |             // if we trust the current step and we are able to use it
 | ||||||
|  |             if (m_learner.is_b_pure (pf) && | ||||||
|  |                 (m.is_asserted(pf) || is_literal(m, fact))) | ||||||
|  |             { | ||||||
|  |                 // just add it to the core
 | ||||||
|  |                 m_learner.add_lemma_to_core(fact); | ||||||
|  |             } | ||||||
|  |             // otherwise recurse on premises
 | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 for (unsigned i = 0, sz =  m_learner.m.get_num_parents(pf); | ||||||
|  |                      i < sz; ++i) | ||||||
|  |                 { | ||||||
|  |                     SASSERT(m_learner.m.is_proof(pf->get_arg(i))); | ||||||
|  |                     proof* premise = m.get_parent (pf, i); | ||||||
|  |                     if (m_learner.is_b_open(premise)) { | ||||||
|  |                         todo.push_back(premise); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #pragma mark - unsat_core_plugin_farkas_lemma | ||||||
|  | void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) | ||||||
|  | { | ||||||
|  |     ast_manager &m = m_learner.m; | ||||||
|  |     SASSERT(m_learner.is_a_marked(step)); | ||||||
|  |     SASSERT(m_learner.is_b_marked(step)); | ||||||
|  |     // XXX this assertion should be true so there is no need to check for it
 | ||||||
|  |     SASSERT (!m_learner.is_closed (step)); | ||||||
|  |     func_decl* d = step->get_decl(); | ||||||
|  |     symbol sym; | ||||||
|  |     if(!m_learner.is_closed(step) && // if step is not already interpolated
 | ||||||
|  |        step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma
 | ||||||
|  |        d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step
 | ||||||
|  |        d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas",
 | ||||||
|  |        d->get_parameter(1).is_symbol(sym) && sym == "farkas" && | ||||||
|  |        d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients
 | ||||||
|  |     { | ||||||
|  |         SASSERT(m_learner.m.has_fact(step)); | ||||||
|  | 
 | ||||||
|  |         ptr_vector<app> literals; | ||||||
|  |         vector<rational> coefficients; | ||||||
|  | 
 | ||||||
|  |         /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and
 | ||||||
|  |          * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which | ||||||
|  |          * is entailed by BP and together with A and BNP entails D. | ||||||
|  |          * | ||||||
|  |          * Let Fark(F) be the farkas coefficient for F. We can use the fact that | ||||||
|  |          * (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1) | ||||||
|  |          * We further have that A+B => C implies (A \land B) => C. (E2) | ||||||
|  |          * | ||||||
|  |          * Alternative 1: | ||||||
|  |          * From (E1) immediately get that BP*Fark(BP) is a solution. | ||||||
|  |          * | ||||||
|  |          * Alternative 2: | ||||||
|  |          * We can rewrite (E2) to rewrite (E1) to | ||||||
|  |          * (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3) | ||||||
|  |          * and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from | ||||||
|  |          * A, BNP and D, we also know that it is inconsisent. Therefore | ||||||
|  |          * neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution. | ||||||
|  |          * | ||||||
|  |          * Finally we also need the following workaround: | ||||||
|  |          * 1) Although we know from theory, that the Farkas coefficients are always nonnegative, | ||||||
|  |          * the Farkas coefficients provided by arith_core are sometimes negative (must be a bug) | ||||||
|  |          * as workaround we take the absolute value of the provided coefficients. | ||||||
|  |          */ | ||||||
|  |         parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient
 | ||||||
|  | 
 | ||||||
|  |         STRACE("spacer.farkas", | ||||||
|  |             verbose_stream() << "Farkas input: "<< "\n"; | ||||||
|  |             for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) | ||||||
|  |             { | ||||||
|  |                 SASSERT(m_learner.m.is_proof(step->get_arg(i))); | ||||||
|  |                 proof *prem = m.get_parent (step, i); | ||||||
|  | 
 | ||||||
|  |                 rational coef; | ||||||
|  |                 VERIFY(params[i].is_rational(coef)); | ||||||
|  | 
 | ||||||
|  |                 bool b_pure = m_learner.is_b_pure (prem); | ||||||
|  |                 verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         bool can_be_closed = true; | ||||||
|  | 
 | ||||||
|  |         for(unsigned i = 0; i < m.get_num_parents(step); ++i) | ||||||
|  |         { | ||||||
|  |             SASSERT(m_learner.m.is_proof(step->get_arg(i))); | ||||||
|  |             proof * premise = m.get_parent (step, i); | ||||||
|  | 
 | ||||||
|  |             if (m_learner.is_b_open (premise)) | ||||||
|  |             { | ||||||
|  |                 SASSERT(!m_learner.is_a_marked(premise)); | ||||||
|  | 
 | ||||||
|  |                 if (m_learner.is_b_pure (step)) | ||||||
|  |                 { | ||||||
|  |                     if (!m_use_constant_from_a) | ||||||
|  |                     { | ||||||
|  |                         rational coefficient; | ||||||
|  |                         VERIFY(params[i].is_rational(coefficient)); | ||||||
|  |                         literals.push_back(to_app(m_learner.m.get_fact(premise))); | ||||||
|  |                         coefficients.push_back(abs(coefficient)); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     can_be_closed = false; | ||||||
|  | 
 | ||||||
|  |                     if (m_use_constant_from_a) | ||||||
|  |                     { | ||||||
|  |                         rational coefficient; | ||||||
|  |                         VERIFY(params[i].is_rational(coefficient)); | ||||||
|  |                         literals.push_back(to_app(m_learner.m.get_fact(premise))); | ||||||
|  |                         coefficients.push_back(abs(coefficient)); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (m_use_constant_from_a) | ||||||
|  |                 { | ||||||
|  |                     rational coefficient; | ||||||
|  |                     VERIFY(params[i].is_rational(coefficient)); | ||||||
|  |                     literals.push_back(to_app(m_learner.m.get_fact(premise))); | ||||||
|  |                     coefficients.push_back(abs(coefficient)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (m_use_constant_from_a) | ||||||
|  |         { | ||||||
|  |             params += m_learner.m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion
 | ||||||
|  | 
 | ||||||
|  |             // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations
 | ||||||
|  |             if (m_learner.m.get_num_parents(step) + 2 < d->get_num_parameters()) | ||||||
|  |             { | ||||||
|  |                 unsigned num_args = 1; | ||||||
|  |                 expr* conclusion = m_learner.m.get_fact(step); | ||||||
|  |                 expr* const* args = &conclusion; | ||||||
|  |                 if (m_learner.m.is_or(conclusion)) | ||||||
|  |                 { | ||||||
|  |                     app* _or = to_app(conclusion); | ||||||
|  |                     num_args = _or->get_num_args(); | ||||||
|  |                     args = _or->get_args(); | ||||||
|  |                 } | ||||||
|  |                 SASSERT(m_learner.m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); | ||||||
|  | 
 | ||||||
|  |                 bool_rewriter brw(m_learner.m); | ||||||
|  |                 for (unsigned i = 0; i < num_args; ++i) | ||||||
|  |                 { | ||||||
|  |                     expr* premise = args[i]; | ||||||
|  | 
 | ||||||
|  |                     expr_ref negatedPremise(m_learner.m); | ||||||
|  |                     brw.mk_not(premise, negatedPremise); | ||||||
|  |                     literals.push_back(to_app(negatedPremise)); | ||||||
|  | 
 | ||||||
|  |                     rational coefficient; | ||||||
|  |                     VERIFY(params[i].is_rational(coefficient)); | ||||||
|  |                     coefficients.push_back(abs(coefficient)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // only if all b-premises can be used directly, add the farkas core and close the step
 | ||||||
|  |         if (can_be_closed) | ||||||
|  |         { | ||||||
|  |             m_learner.set_closed(step, true); | ||||||
|  | 
 | ||||||
|  |             expr_ref res(m_learner.m); | ||||||
|  |             compute_linear_combination(coefficients, literals, res); | ||||||
|  | 
 | ||||||
|  |             m_learner.add_lemma_to_core(res); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res) | ||||||
|  | { | ||||||
|  |     SASSERT(literals.size() == coefficients.size()); | ||||||
|  | 
 | ||||||
|  |     ast_manager& m = res.get_manager(); | ||||||
|  |     smt::farkas_util util(m); | ||||||
|  |     if (m_use_constant_from_a) | ||||||
|  |     { | ||||||
|  |         util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints
 | ||||||
|  |     } | ||||||
|  |     for(unsigned i = 0; i < literals.size(); ++i) | ||||||
|  |     { | ||||||
|  |         util.add(coefficients[i], literals[i]); | ||||||
|  |     } | ||||||
|  |     if (m_use_constant_from_a) | ||||||
|  |     { | ||||||
|  |         res = util.get(); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         expr_ref negated_linear_combination = util.get(); | ||||||
|  |         res = mk_not(m, negated_linear_combination); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #pragma mark - unsat_core_plugin_farkas_optimized | ||||||
|  |     void unsat_core_plugin_farkas_lemma_optimized::compute_partial_core(proof* step) | ||||||
|  |     { | ||||||
|  |         SASSERT(m_learner.is_a_marked(step)); | ||||||
|  |         SASSERT(m_learner.is_b_marked(step)); | ||||||
|  | 
 | ||||||
|  |         func_decl* d = step->get_decl(); | ||||||
|  |         symbol sym; | ||||||
|  |         if(!m_learner.is_closed(step) && // if step is not already interpolated
 | ||||||
|  |            step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma
 | ||||||
|  |            d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step
 | ||||||
|  |            d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas",
 | ||||||
|  |            d->get_parameter(1).is_symbol(sym) && sym == "farkas" && | ||||||
|  |            d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients
 | ||||||
|  |         { | ||||||
|  |             SASSERT(m_learner.m.has_fact(step)); | ||||||
|  | 
 | ||||||
|  |             vector<std::pair<app*,rational> > linear_combination; // collects all summands of the linear combination
 | ||||||
|  | 
 | ||||||
|  |             parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient
 | ||||||
|  | 
 | ||||||
|  |             STRACE("spacer.farkas", | ||||||
|  |                verbose_stream() << "Farkas input: "<< "\n"; | ||||||
|  |                for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) | ||||||
|  |                { | ||||||
|  |                    SASSERT(m_learner.m.is_proof(step->get_arg(i))); | ||||||
|  |                    proof *prem = m.get_parent (step, i); | ||||||
|  | 
 | ||||||
|  |                    rational coef; | ||||||
|  |                    VERIFY(params[i].is_rational(coef)); | ||||||
|  | 
 | ||||||
|  |                    bool b_pure = m_learner.is_b_pure (prem); | ||||||
|  |                    verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; | ||||||
|  |                } | ||||||
|  |             ); | ||||||
|  | 
 | ||||||
|  |             bool can_be_closed = true; | ||||||
|  |             for(unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) | ||||||
|  |             { | ||||||
|  |                 SASSERT(m_learner.m.is_proof(step->get_arg(i))); | ||||||
|  |                 proof * premise = m.get_parent (step, i); | ||||||
|  | 
 | ||||||
|  |                 if (m_learner.is_b_marked(premise) && !m_learner.is_closed(premise)) | ||||||
|  |                 { | ||||||
|  |                     SASSERT(!m_learner.is_a_marked(premise)); | ||||||
|  | 
 | ||||||
|  |                     // XXX AG: why is this condition is based on step and not on premise?
 | ||||||
|  |                     if (m_learner.only_contains_symbols_b(m_learner.m.get_fact(step)) && !m_learner.is_h_marked(step)) | ||||||
|  |                     { | ||||||
|  |                         rational coefficient; | ||||||
|  |                         VERIFY(params[i].is_rational(coefficient)); | ||||||
|  |                         linear_combination.push_back(std::make_pair(to_app(m_learner.m.get_fact(premise)), abs(coefficient))); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         can_be_closed = false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // only if all b-premises can be used directly, close the step and add linear combinations for later processing
 | ||||||
|  |             if (can_be_closed) | ||||||
|  |             { | ||||||
|  |                 m_learner.set_closed(step, true); | ||||||
|  |                 if (!linear_combination.empty()) | ||||||
|  |                 { | ||||||
|  |                     m_linear_combinations.push_back(linear_combination); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     struct farkas_optimized_less_than_pairs | ||||||
|  |     { | ||||||
|  |         inline bool operator() (const std::pair<app*,rational>& pair1, const std::pair<app*,rational>& pair2) const | ||||||
|  |         { | ||||||
|  |             return (pair1.first->get_id() < pair2.first->get_id()); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_farkas_lemma_optimized::finalize() | ||||||
|  |     { | ||||||
|  |         if(m_linear_combinations.empty()) | ||||||
|  |         { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         DEBUG_CODE( | ||||||
|  |             for (auto& linear_combination : m_linear_combinations) { | ||||||
|  |             SASSERT(linear_combination.size() > 0); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |         // 1. construct ordered basis
 | ||||||
|  |         ptr_vector<app> ordered_basis; | ||||||
|  |         obj_map<app, unsigned> map; | ||||||
|  |         unsigned counter = 0; | ||||||
|  |         for (const auto& linear_combination : m_linear_combinations) | ||||||
|  |         { | ||||||
|  |             for (const auto& pair : linear_combination) | ||||||
|  |             { | ||||||
|  |                 if (!map.contains(pair.first)) | ||||||
|  |                 { | ||||||
|  |                     ordered_basis.push_back(pair.first); | ||||||
|  |                     map.insert(pair.first, counter++); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 2. populate matrix
 | ||||||
|  |         spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size()); | ||||||
|  | 
 | ||||||
|  |         for (unsigned i=0; i < m_linear_combinations.size(); ++i) | ||||||
|  |         { | ||||||
|  |             auto linear_combination = m_linear_combinations[i]; | ||||||
|  |             for (const auto& pair : linear_combination) | ||||||
|  |             { | ||||||
|  |                 matrix.set(i, map[pair.first], pair.second); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 3. perform gaussian elimination
 | ||||||
|  |         unsigned i = matrix.perform_gaussian_elimination(); | ||||||
|  | 
 | ||||||
|  |         // 4. extract linear combinations from matrix and add result to core
 | ||||||
|  |         for (unsigned k=0; k < i; k++)// i points to the row after the last row which is non-zero
 | ||||||
|  |         { | ||||||
|  |             ptr_vector<app> literals; | ||||||
|  |             vector<rational> coefficients; | ||||||
|  |             for (unsigned l=0; l < matrix.num_cols(); ++l) | ||||||
|  |             { | ||||||
|  |                 if (!matrix.get(k,l).is_zero()) | ||||||
|  |                 { | ||||||
|  |                     literals.push_back(ordered_basis[l]); | ||||||
|  |                     coefficients.push_back(matrix.get(k,l)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |             SASSERT(literals.size() > 0); | ||||||
|  |             expr_ref linear_combination(m); | ||||||
|  |             compute_linear_combination(coefficients, literals, linear_combination); | ||||||
|  | 
 | ||||||
|  |             m_learner.add_lemma_to_core(linear_combination); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res) | ||||||
|  |             { | ||||||
|  |         SASSERT(literals.size() == coefficients.size()); | ||||||
|  | 
 | ||||||
|  |         ast_manager& m = res.get_manager(); | ||||||
|  |         smt::farkas_util util(m); | ||||||
|  |         for(unsigned i = 0; i < literals.size(); ++i) | ||||||
|  |                     { | ||||||
|  |             util.add(coefficients[i], literals[i]); | ||||||
|  |         } | ||||||
|  |         expr_ref negated_linear_combination = util.get(); | ||||||
|  |         SASSERT(m.is_not(negated_linear_combination)); | ||||||
|  |         res = mk_not(m, negated_linear_combination); //TODO: rewrite the get-method to return nonnegated stuff?
 | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  | #pragma mark - unsat_core_plugin_farkas_bounded | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_farkas_lemma_bounded::finalize() | ||||||
|  |                     { | ||||||
|  |         if(m_linear_combinations.empty()) | ||||||
|  |         {return;} | ||||||
|  |         DEBUG_CODE( | ||||||
|  |             for (auto& linear_combination : m_linear_combinations) { | ||||||
|  |                 SASSERT(linear_combination.size() > 0); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |         // 1. construct ordered basis
 | ||||||
|  |         ptr_vector<app> ordered_basis; | ||||||
|  |         obj_map<app, unsigned> map; | ||||||
|  |         unsigned counter = 0; | ||||||
|  |         for (const auto& linear_combination : m_linear_combinations) | ||||||
|  |                 { | ||||||
|  |             for (const auto& pair : linear_combination) | ||||||
|  |         { | ||||||
|  |                 if (!map.contains(pair.first)) | ||||||
|  |             { | ||||||
|  |                     ordered_basis.push_back(pair.first); | ||||||
|  |                     map.insert(pair.first, counter++); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 2. populate matrix
 | ||||||
|  |         spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size()); | ||||||
|  | 
 | ||||||
|  |         for (unsigned i=0; i < m_linear_combinations.size(); ++i) | ||||||
|  |             { | ||||||
|  |             auto linear_combination = m_linear_combinations[i]; | ||||||
|  |             for (const auto& pair : linear_combination) | ||||||
|  |         { | ||||||
|  |                 matrix.set(i, map[pair.first], pair.second); | ||||||
|  |             } | ||||||
|  |             } | ||||||
|  |         matrix.print_matrix(); | ||||||
|  | 
 | ||||||
|  |         // 3. normalize matrix to integer values
 | ||||||
|  |         matrix.normalize(); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         arith_util util(m); | ||||||
|  | 
 | ||||||
|  |         vector<expr_ref_vector> coeffs; | ||||||
|  |         for (unsigned i=0; i < matrix.num_rows(); ++i) | ||||||
|  |             { | ||||||
|  |             coeffs.push_back(expr_ref_vector(m)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         vector<expr_ref_vector> bounded_vectors; | ||||||
|  |         for (unsigned j=0; j < matrix.num_cols(); ++j) | ||||||
|  |         { | ||||||
|  |             bounded_vectors.push_back(expr_ref_vector(m)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 4. find smallest n using guess and check algorithm
 | ||||||
|  |         for(unsigned n = 1; true; ++n) | ||||||
|  |         { | ||||||
|  |             params_ref p; | ||||||
|  |             p.set_bool("model", true); | ||||||
|  |             scoped_ptr<solver> s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version?
 | ||||||
|  | 
 | ||||||
|  |             // add new variables w_in,
 | ||||||
|  |             for (unsigned i=0; i < matrix.num_rows(); ++i) | ||||||
|  |             { | ||||||
|  |                 std::string name = "w_" + std::to_string(i) + std::to_string(n); | ||||||
|  | 
 | ||||||
|  |                 func_decl_ref decl(m); | ||||||
|  |                 decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)0, util.mk_int()); | ||||||
|  |                 coeffs[i].push_back(m.mk_const(decl)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // we need s_jn
 | ||||||
|  |             for (unsigned j=0; j < matrix.num_cols(); ++j) | ||||||
|  |             { | ||||||
|  |                 std::string name = "s_" + std::to_string(j) + std::to_string(n); | ||||||
|  | 
 | ||||||
|  |                 func_decl_ref decl(m); | ||||||
|  |                 decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)0, util.mk_int()); | ||||||
|  | 
 | ||||||
|  |                 expr_ref s_jn(m); | ||||||
|  |                 s_jn = m.mk_const(decl); | ||||||
|  | 
 | ||||||
|  |                 bounded_vectors[j].push_back(s_jn); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // assert bounds for all s_jn
 | ||||||
|  |             for (unsigned l=0; l < n; ++l) | ||||||
|  |                 { | ||||||
|  |                 for (unsigned j=0; j < matrix.num_cols(); ++j) | ||||||
|  |                     { | ||||||
|  |                     expr* s_jn = bounded_vectors[j][l].get(); | ||||||
|  | 
 | ||||||
|  |                     expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m); | ||||||
|  |                     expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m); | ||||||
|  |                     s->assert_expr(lb); | ||||||
|  |                     s->assert_expr(ub); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             // assert: forall i,j: a_ij = sum_k w_ik * s_jk
 | ||||||
|  |             for (unsigned i=0; i < matrix.num_rows(); ++i) | ||||||
|  |                 { | ||||||
|  |                 for (unsigned j=0; j < matrix.num_cols(); ++j) | ||||||
|  |                     { | ||||||
|  |                     SASSERT(matrix.get(i, j).is_int()); | ||||||
|  |                     app_ref a_ij(util.mk_numeral(matrix.get(i,j), true),m); | ||||||
|  | 
 | ||||||
|  |                     app_ref sum(m); | ||||||
|  |                     sum = util.mk_int(0); | ||||||
|  |                     for (int k=0; k < n; ++k) | ||||||
|  |                         { | ||||||
|  |                         sum = util.mk_add(sum, util.mk_mul(coeffs[i][k].get(), bounded_vectors[j][k].get())); | ||||||
|  |                         } | ||||||
|  |                     expr_ref eq(m.mk_eq(a_ij, sum),m); | ||||||
|  |                     s->assert_expr(eq); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             // check result
 | ||||||
|  |             lbool res = s->check_sat(0,0); | ||||||
|  | 
 | ||||||
|  |             // if sat extract model and add corresponding linear combinations to core
 | ||||||
|  |             if (res == lbool::l_true) | ||||||
|  |                 { | ||||||
|  |                 model_ref model; | ||||||
|  |                 s->get_model(model); | ||||||
|  | 
 | ||||||
|  |                 for (int k=0; k < n; ++k) | ||||||
|  |         { | ||||||
|  |             ptr_vector<app> literals; | ||||||
|  |             vector<rational> coefficients; | ||||||
|  |                     for (int j=0; j < matrix.num_cols(); ++j) | ||||||
|  |             { | ||||||
|  |                         expr_ref evaluation(m); | ||||||
|  | 
 | ||||||
|  |                         model.get()->eval(bounded_vectors[j][k].get(), evaluation, false); | ||||||
|  |                         if (!util.is_zero(evaluation)) | ||||||
|  |                 { | ||||||
|  |                             literals.push_back(ordered_basis[j]); | ||||||
|  |                             coefficients.push_back(rational(1)); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |                     SASSERT(!literals.empty()); // since then previous outer loop would have found solution already
 | ||||||
|  |             expr_ref linear_combination(m); | ||||||
|  |             compute_linear_combination(coefficients, literals, linear_combination); | ||||||
|  | 
 | ||||||
|  |             m_learner.add_lemma_to_core(linear_combination); | ||||||
|  |         } | ||||||
|  |                 return; | ||||||
|  |     } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #pragma mark - unsat_core_plugin_min_cut | ||||||
|  |     unsat_core_plugin_min_cut::unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m){} | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_min_cut::compute_partial_core(proof* step) | ||||||
|  |     { | ||||||
|  |         ptr_vector<proof> todo; | ||||||
|  | 
 | ||||||
|  |         SASSERT(m_learner.is_a_marked(step)); | ||||||
|  |         SASSERT(m_learner.is_b_marked(step)); | ||||||
|  |         SASSERT(m.get_num_parents(step) > 0); | ||||||
|  |         SASSERT(!m_learner.is_closed(step)); | ||||||
|  |         todo.push_back(step); | ||||||
|  | 
 | ||||||
|  |         while (!todo.empty()) | ||||||
|  |         { | ||||||
|  |             proof* current = todo.back(); | ||||||
|  |             todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |             if (!m_learner.is_closed(current) && !m_visited.is_marked(current)) | ||||||
|  |             { | ||||||
|  |                 m_visited.mark(current, true); | ||||||
|  |                 advance_to_lowest_partial_cut(current, todo); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         m_learner.set_closed(step, true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_min_cut::advance_to_lowest_partial_cut(proof* step, ptr_vector<proof>& todo2) | ||||||
|  |     { | ||||||
|  |         bool is_sink = true; | ||||||
|  | 
 | ||||||
|  |         ast_manager &m = m_learner.m; | ||||||
|  |         ptr_vector<proof> todo; | ||||||
|  | 
 | ||||||
|  |         for (unsigned i = 0, sz = m.get_num_parents(step); i < sz; ++i) | ||||||
|  |         { | ||||||
|  |             proof* premise = m.get_parent (step, i); | ||||||
|  |             { | ||||||
|  |                 if (m_learner.is_b_marked(premise)) | ||||||
|  |                 { | ||||||
|  |                     todo.push_back(premise); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         while (!todo.empty()) | ||||||
|  |         { | ||||||
|  |             proof* current = todo.back(); | ||||||
|  |             todo.pop_back(); | ||||||
|  | 
 | ||||||
|  |             // if current step hasn't been processed,
 | ||||||
|  |             if (!m_learner.is_closed(current)) | ||||||
|  |             { | ||||||
|  |                 SASSERT(!m_learner.is_a_marked(current)); // by I.H. the step must be already visited
 | ||||||
|  | 
 | ||||||
|  |                 // and the current step needs to be interpolated:
 | ||||||
|  |                 if (m_learner.is_b_marked(current)) | ||||||
|  |                 { | ||||||
|  |                     // if we trust the current step and we are able to use it
 | ||||||
|  |                     if (m_learner.is_b_pure (current) && | ||||||
|  |                         (m.is_asserted(current) || | ||||||
|  |                          is_literal(m, m.get_fact(current)))) | ||||||
|  |                     { | ||||||
|  |                         // add corresponding edges and continue original traversel
 | ||||||
|  |                         if (m_learner.is_a_marked(step)) | ||||||
|  |                         { | ||||||
|  |                             add_edge(nullptr, current); // current is sink
 | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             add_edge(step, current); | ||||||
|  |                         } | ||||||
|  |                         todo2.push_back(current); | ||||||
|  |                         is_sink = false; | ||||||
|  |                     } | ||||||
|  |                     // otherwise recurse on premises
 | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         for (unsigned i = 0; i < m_learner.m.get_num_parents(current); ++i) | ||||||
|  |                         { | ||||||
|  |                             SASSERT(m_learner.m.is_proof(current->get_arg(i))); | ||||||
|  |                             proof* premise = m.get_parent (current, i); | ||||||
|  |                             todo.push_back(premise); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (is_sink) | ||||||
|  |         { | ||||||
|  |             add_edge(step, nullptr); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_min_cut::add_edge(proof* i, proof* j) | ||||||
|  |     { | ||||||
|  |         unsigned node_i; | ||||||
|  |         unsigned node_j; | ||||||
|  |         if (i == nullptr) | ||||||
|  |         { | ||||||
|  |             node_i = 0; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             unsigned tmp; | ||||||
|  |             if (m_proof_to_node_plus.find(i, tmp)) | ||||||
|  |             { | ||||||
|  |                 node_i = tmp; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 unsigned node_other = m_min_cut.new_node(); | ||||||
|  |                 node_i = m_min_cut.new_node(); | ||||||
|  | 
 | ||||||
|  |                 m_proof_to_node_minus.insert(i, node_other); | ||||||
|  |                 m_proof_to_node_plus.insert(i, node_i); | ||||||
|  | 
 | ||||||
|  |                 if (node_i >= m_node_to_formula.size()) | ||||||
|  |                 { | ||||||
|  |                     m_node_to_formula.resize(node_i + 1); | ||||||
|  |                 } | ||||||
|  |                 m_node_to_formula[node_other] = m.get_fact(i); | ||||||
|  |                 m_node_to_formula[node_i] = m.get_fact(i); | ||||||
|  | 
 | ||||||
|  |                 m_min_cut.add_edge(node_other, node_i, 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (j == nullptr) | ||||||
|  |         { | ||||||
|  |             node_j = 1; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             unsigned tmp; | ||||||
|  |             if (m_proof_to_node_minus.find(j, tmp)) | ||||||
|  |             { | ||||||
|  |                 node_j = tmp; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 node_j = m_min_cut.new_node(); | ||||||
|  |                 unsigned node_other = m_min_cut.new_node(); | ||||||
|  | 
 | ||||||
|  |                 m_proof_to_node_minus.insert(j, node_j); | ||||||
|  |                 m_proof_to_node_plus.insert(j, node_other); | ||||||
|  | 
 | ||||||
|  |                 if (node_other >= m_node_to_formula.size()) | ||||||
|  |                 { | ||||||
|  |                     m_node_to_formula.resize(node_other + 1); | ||||||
|  |                 } | ||||||
|  |                 m_node_to_formula[node_j] = m.get_fact(j); | ||||||
|  |                 m_node_to_formula[node_other] = m.get_fact(j); | ||||||
|  | 
 | ||||||
|  |                 m_min_cut.add_edge(node_j, node_other, 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // finally connect nodes
 | ||||||
|  |         m_min_cut.add_edge(node_i, node_j, 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void unsat_core_plugin_min_cut::finalize() | ||||||
|  |     { | ||||||
|  |         vector<unsigned int> cut_nodes; | ||||||
|  |         m_min_cut.compute_min_cut(cut_nodes); | ||||||
|  | 
 | ||||||
|  |         for (unsigned cut_node : cut_nodes) | ||||||
|  |         { | ||||||
|  |             m_learner.add_lemma_to_core(m_node_to_formula[cut_node]); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | } | ||||||
							
								
								
									
										115
									
								
								src/muz/spacer/spacer_unsat_core_plugin.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/muz/spacer/spacer_unsat_core_plugin.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_unsat_core_plugin.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  |    plugin for itp cores | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  |     Bernhard Gleiss | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef _SPACER_UNSAT_CORE_PLUGIN_H_ | ||||||
|  | #define _SPACER_UNSAT_CORE_PLUGIN_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | #include "spacer_min_cut.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | class unsat_core_learner; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class unsat_core_plugin { | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     unsat_core_plugin(unsat_core_learner& learner) : m_learner(learner){}; | ||||||
|  |     virtual ~unsat_core_plugin(){}; | ||||||
|  |     virtual void compute_partial_core(proof* step) = 0; | ||||||
|  |     virtual void finalize(){}; | ||||||
|  | 
 | ||||||
|  |     unsat_core_learner& m_learner; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class unsat_core_plugin_lemma : public unsat_core_plugin { | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; | ||||||
|  | 
 | ||||||
|  |     virtual void compute_partial_core(proof* step) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void add_lowest_split_to_core(proof* step) const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, bool split_literals, bool use_constant_from_a=true) : unsat_core_plugin(learner), m_split_literals(split_literals), m_use_constant_from_a(use_constant_from_a) {}; | ||||||
|  | 
 | ||||||
|  |     virtual void compute_partial_core(proof* step) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     bool m_split_literals; | ||||||
|  |     bool m_use_constant_from_a; | ||||||
|  |     /*
 | ||||||
|  |      * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res | ||||||
|  |      */ | ||||||
|  |     void compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  |     class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m) {}; | ||||||
|  | 
 | ||||||
|  |         virtual void compute_partial_core(proof* step) override; | ||||||
|  |         virtual void finalize() override; | ||||||
|  | 
 | ||||||
|  |     protected: | ||||||
|  |         vector<vector<std::pair<app*, rational> > > m_linear_combinations; | ||||||
|  |         ast_manager& m; | ||||||
|  |         /*
 | ||||||
|  |          * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res | ||||||
|  |          */ | ||||||
|  |         void compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class unsat_core_plugin_farkas_lemma_bounded : public unsat_core_plugin_farkas_lemma_optimized { | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         unsat_core_plugin_farkas_lemma_bounded(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin_farkas_lemma_optimized(learner, m) {}; | ||||||
|  | 
 | ||||||
|  |         virtual void finalize() override; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     class unsat_core_plugin_min_cut : public unsat_core_plugin { | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m); | ||||||
|  | 
 | ||||||
|  |         virtual void compute_partial_core(proof* step) override; | ||||||
|  |         virtual void finalize() override; | ||||||
|  |     private: | ||||||
|  |         ast_manager& m; | ||||||
|  | 
 | ||||||
|  |         ast_mark m_visited; // saves for each node i whether the subproof with root i has already been added to the min-cut-problem
 | ||||||
|  |         obj_map<proof, unsigned> m_proof_to_node_minus; // maps proof-steps to the corresponding minus-nodes (the ones which are closer to source)
 | ||||||
|  |         obj_map<proof, unsigned> m_proof_to_node_plus; // maps proof-steps to the corresponding plus-nodes (the ones which are closer to sink)
 | ||||||
|  |         void advance_to_lowest_partial_cut(proof* step, ptr_vector<proof>& todo2); | ||||||
|  |         void add_edge(proof* i, proof* j); | ||||||
|  | 
 | ||||||
|  |         vector<expr*> m_node_to_formula; // maps each node to the corresponding formula in the original proof
 | ||||||
|  | 
 | ||||||
|  |         spacer_min_cut m_min_cut; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | #endif | ||||||
							
								
								
									
										1393
									
								
								src/muz/spacer/spacer_util.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1393
									
								
								src/muz/spacer/spacer_util.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										180
									
								
								src/muz/spacer/spacer_util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/muz/spacer/spacer_util.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,180 @@ | ||||||
|  | /*++
 | ||||||
|  | Copyright (c) 2011 Microsoft Corporation | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_util.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |     Utility functions for SPACER. | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Krystof Hoder (t-khoder) 2011-8-19. | ||||||
|  |     Arie Gurfinkel | ||||||
|  |     Anvesh Komuravelli | ||||||
|  | 
 | ||||||
|  | Revision History: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #ifndef _SPACER_UTIL_H_ | ||||||
|  | #define _SPACER_UTIL_H_ | ||||||
|  | 
 | ||||||
|  | #include "ast.h" | ||||||
|  | #include "ast_pp.h" | ||||||
|  | #include "obj_hashtable.h" | ||||||
|  | #include "ref_vector.h" | ||||||
|  | #include "simplifier.h" | ||||||
|  | #include "trace.h" | ||||||
|  | #include "vector.h" | ||||||
|  | #include "arith_decl_plugin.h" | ||||||
|  | #include "array_decl_plugin.h" | ||||||
|  | #include "bv_decl_plugin.h" | ||||||
|  | #include "model.h" | ||||||
|  | 
 | ||||||
|  | #include "stopwatch.h" | ||||||
|  | #include "spacer_antiunify.h" | ||||||
|  | 
 | ||||||
|  | class model; | ||||||
|  | class model_core; | ||||||
|  | class model_evaluator; | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | 
 | ||||||
|  | inline unsigned infty_level () {return UINT_MAX;} | ||||||
|  | 
 | ||||||
|  | inline bool is_infty_level(unsigned lvl) | ||||||
|  | { return lvl == infty_level (); } | ||||||
|  | 
 | ||||||
|  | inline unsigned next_level(unsigned lvl) | ||||||
|  | { return is_infty_level(lvl)?lvl:(lvl+1); } | ||||||
|  | 
 | ||||||
|  | inline unsigned prev_level (unsigned lvl) | ||||||
|  | { | ||||||
|  |     if(is_infty_level(lvl)) { return infty_level(); } | ||||||
|  |     if(lvl == 0) { return 0; } | ||||||
|  |     return lvl -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct pp_level { | ||||||
|  |     unsigned m_level; | ||||||
|  |     pp_level(unsigned l): m_level(l) {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | inline std::ostream& operator<<(std::ostream& out, pp_level const& p) | ||||||
|  | { | ||||||
|  |     if (is_infty_level(p.m_level)) { | ||||||
|  |         return out << "oo"; | ||||||
|  |     } else { | ||||||
|  |         return out << p.m_level; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct scoped_watch { | ||||||
|  |     stopwatch &m_sw; | ||||||
|  |     scoped_watch (stopwatch &sw, bool reset=false): m_sw(sw) | ||||||
|  |         { | ||||||
|  |             if(reset) { m_sw.reset(); } | ||||||
|  |             m_sw.start (); | ||||||
|  |         } | ||||||
|  |     ~scoped_watch () {m_sw.stop ();} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef ptr_vector<app> app_vector; | ||||||
|  | typedef ptr_vector<func_decl> decl_vector; | ||||||
|  | typedef obj_hashtable<func_decl> func_decl_set; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class model_evaluator_util { | ||||||
|  |     ast_manager&           m; | ||||||
|  |     model_ref              m_model; | ||||||
|  |     model_evaluator*       m_mev; | ||||||
|  | 
 | ||||||
|  |     /// initialize with a given model. All previous state is lost. model can be NULL
 | ||||||
|  |     void reset (model *model); | ||||||
|  | public: | ||||||
|  |     model_evaluator_util(ast_manager& m); | ||||||
|  |     ~model_evaluator_util(); | ||||||
|  | 
 | ||||||
|  |     void set_model(model &model) {reset (&model);} | ||||||
|  |     model_ref &get_model() {return m_model;} | ||||||
|  |     ast_manager& get_ast_manager() const {return m;} | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     bool is_true (const expr_ref_vector &v); | ||||||
|  |     bool is_false(expr* x); | ||||||
|  |     bool is_true(expr* x); | ||||||
|  | 
 | ||||||
|  |     bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion); | ||||||
|  |     /// evaluates an expression
 | ||||||
|  |     bool eval (expr *e, expr_ref &result, bool model_completion); | ||||||
|  |     // expr_ref eval(expr* e, bool complete=true);
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |    \brief replace variables that are used in many disequalities by | ||||||
|  |    an equality using the model. | ||||||
|  | 
 | ||||||
|  |    Assumption: the model satisfies the conjunctions. | ||||||
|  | */ | ||||||
|  | void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |    \brief hoist non-boolean if expressions. | ||||||
|  | */ | ||||||
|  | void hoist_non_bool_if(expr_ref& fml); | ||||||
|  | 
 | ||||||
|  | bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); | ||||||
|  | 
 | ||||||
|  | bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * do the following in sequence | ||||||
|  |  * 1. use qe_lite to cheaply eliminate vars | ||||||
|  |  * 2. for remaining boolean vars, substitute using M | ||||||
|  |  * 3. use MBP for remaining array and arith variables | ||||||
|  |  * 4. for any remaining arith variables, substitute using M | ||||||
|  |  */ | ||||||
|  | void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, | ||||||
|  |                  const model_ref& M, bool reduce_all_selects=false, bool native_mbp=false, | ||||||
|  |                  bool dont_sub=false); | ||||||
|  | 
 | ||||||
|  | void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); | ||||||
|  | 
 | ||||||
|  | void expand_literals(ast_manager &m, expr_ref_vector& conjs); | ||||||
|  | void compute_implicant_literals (model_evaluator_util &mev, | ||||||
|  |                                  expr_ref_vector &formula, expr_ref_vector &res); | ||||||
|  | void simplify_bounds (expr_ref_vector &lemmas); | ||||||
|  | void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); | ||||||
|  | 
 | ||||||
|  | /** ground expression by replacing all free variables by skolem constants */ | ||||||
|  | void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml); | ||||||
|  | 
 | ||||||
|  | bool contains_selects (expr* fml, ast_manager& m); | ||||||
|  | void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); | ||||||
|  | 
 | ||||||
|  | void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); | ||||||
|  | 
 | ||||||
|  | /** extended pretty-printer
 | ||||||
|  |  * used for debugging | ||||||
|  |  * disables aliasing of common sub-expressions | ||||||
|  | */ | ||||||
|  | struct mk_epp : public mk_pp { | ||||||
|  |     params_ref m_epp_params; | ||||||
|  |     expr_ref m_epp_expr; | ||||||
|  |     mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); | ||||||
|  |     void rw(expr *e, expr_ref &out); | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										519
									
								
								src/muz/spacer/spacer_virtual_solver.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								src/muz/spacer/spacer_virtual_solver.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,519 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_virtual_solver.cpp | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    multi-solver view of a single smt::kernel | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | 
 | ||||||
|  | #include "spacer_virtual_solver.h" | ||||||
|  | #include "ast_util.h" | ||||||
|  | #include "ast_pp_util.h" | ||||||
|  | #include "spacer_util.h" | ||||||
|  | #include "bool_rewriter.h" | ||||||
|  | 
 | ||||||
|  | #include "proof_checker.h" | ||||||
|  | 
 | ||||||
|  | #include "scoped_proof.h" | ||||||
|  | 
 | ||||||
|  | namespace spacer { | ||||||
|  | virtual_solver::virtual_solver(virtual_solver_factory &factory, | ||||||
|  |                                smt::kernel &context, app* pred) : | ||||||
|  |     solver_na2as(context.m()), | ||||||
|  |     m_factory(factory), | ||||||
|  |     m(context.m()), | ||||||
|  |     m_context(context), | ||||||
|  |     m_pred(pred, m), | ||||||
|  |     m_virtual(!m.is_true(pred)), | ||||||
|  |     m_assertions(m), | ||||||
|  |     m_head(0), | ||||||
|  |     m_flat(m), | ||||||
|  |     m_pushed(false), | ||||||
|  |     m_in_delay_scope(false), | ||||||
|  |     m_dump_benchmarks(factory.fparams().m_dump_benchmarks), | ||||||
|  |     m_dump_counter(0), | ||||||
|  |     m_proof(m) | ||||||
|  | { | ||||||
|  |     // -- insert m_pred->true background assumption this will not
 | ||||||
|  |     // -- change m_context, but will add m_pred to
 | ||||||
|  |     // -- the private field solver_na2as::m_assumptions
 | ||||||
|  |     if (m_virtual) | ||||||
|  |     { solver_na2as::assert_expr(m.mk_true(), m_pred); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | virtual_solver::~virtual_solver() | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed || get_scope_level() > 0); | ||||||
|  |     if (m_pushed) { pop(get_scope_level()); } | ||||||
|  | 
 | ||||||
|  |     if (m_virtual) { | ||||||
|  |         m_pred = m.mk_not(m_pred); | ||||||
|  |         m_context.assert_expr(m_pred); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | static bool matches_fact(expr_ref_vector &args, expr* &match) | ||||||
|  | { | ||||||
|  |     ast_manager &m = args.get_manager(); | ||||||
|  |     expr *fact = args.back(); | ||||||
|  |     for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) { | ||||||
|  |         expr *arg = args.get(i); | ||||||
|  |         if (m.is_proof(arg) && | ||||||
|  |                 m.has_fact(to_app(arg)) && | ||||||
|  |                 m.get_fact(to_app(arg)) == fact) { | ||||||
|  |             match = arg; | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class elim_aux_assertions { | ||||||
|  |     app_ref m_aux; | ||||||
|  | public: | ||||||
|  |     elim_aux_assertions(app_ref aux) : m_aux(aux) {} | ||||||
|  | 
 | ||||||
|  |     void mk_or_core(expr_ref_vector &args, expr_ref &res) | ||||||
|  |     { | ||||||
|  |         ast_manager &m = args.get_manager(); | ||||||
|  |         unsigned j = 0; | ||||||
|  |         for (unsigned i = 0, sz = args.size(); i < sz; ++i) { | ||||||
|  |             if (m.is_false(args.get(i))) { continue; } | ||||||
|  |             if (i != j) { args [j] = args.get(i); } | ||||||
|  |             ++j; | ||||||
|  |         } | ||||||
|  |         SASSERT(j >= 1); | ||||||
|  |         res = j > 1 ? m.mk_or(j, args.c_ptr()) : args.get(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void mk_app(func_decl *decl, expr_ref_vector &args, expr_ref &res) | ||||||
|  |     { | ||||||
|  |         ast_manager &m = args.get_manager(); | ||||||
|  |         bool_rewriter brwr(m); | ||||||
|  | 
 | ||||||
|  |         if (m.is_or(decl)) | ||||||
|  |         { mk_or_core(args, res); } | ||||||
|  |         else if (m.is_iff(decl) && args.size() == 2) | ||||||
|  |             // avoiding simplifying equalities. In particular,
 | ||||||
|  |             // we don't want (= (not a) (not b)) to be reduced to (= a b)
 | ||||||
|  |         { res = m.mk_iff(args.get(0), args.get(1)); } | ||||||
|  |         else | ||||||
|  |         { brwr.mk_app(decl, args.size(), args.c_ptr(), res); } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void operator()(ast_manager &m, proof *pr, proof_ref &res) | ||||||
|  |     { | ||||||
|  |         DEBUG_CODE(proof_checker pc(m); | ||||||
|  |                    expr_ref_vector side(m); | ||||||
|  |                    SASSERT(pc.check(pr, side)); | ||||||
|  |                   ); | ||||||
|  |         obj_map<app, app*> cache; | ||||||
|  |         bool_rewriter brwr(m); | ||||||
|  | 
 | ||||||
|  |         // for reference counting of new proofs
 | ||||||
|  |         app_ref_vector pinned(m); | ||||||
|  | 
 | ||||||
|  |         ptr_vector<app> todo; | ||||||
|  |         todo.push_back(pr); | ||||||
|  | 
 | ||||||
|  |         expr_ref not_aux(m); | ||||||
|  |         not_aux = m.mk_not(m_aux); | ||||||
|  | 
 | ||||||
|  |         expr_ref_vector args(m); | ||||||
|  | 
 | ||||||
|  |         while (!todo.empty()) { | ||||||
|  |             app *p, *r; | ||||||
|  |             expr *a; | ||||||
|  | 
 | ||||||
|  |             p = todo.back(); | ||||||
|  |             if (cache.find(pr, r)) { | ||||||
|  |                 todo.pop_back(); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             SASSERT(!todo.empty() || pr == p); | ||||||
|  |             bool dirty = false; | ||||||
|  |             unsigned todo_sz = todo.size(); | ||||||
|  |             args.reset(); | ||||||
|  |             for (unsigned i = 0, sz = p->get_num_args(); i < sz; ++i) { | ||||||
|  |                 expr* arg = p->get_arg(i); | ||||||
|  |                 if (arg == m_aux.get()) { | ||||||
|  |                     dirty = true; | ||||||
|  |                     args.push_back(m.mk_true()); | ||||||
|  |                 } else if (arg == not_aux.get()) { | ||||||
|  |                     dirty = true; | ||||||
|  |                     args.push_back(m.mk_false()); | ||||||
|  |                 } | ||||||
|  |                 // skip (asserted m_aux)
 | ||||||
|  |                 else if (m.is_asserted(arg, a) && a == m_aux.get()) { | ||||||
|  |                     dirty = true; | ||||||
|  |                 } | ||||||
|  |                 // skip (hypothesis m_aux)
 | ||||||
|  |                 else if (m.is_hypothesis(arg, a) && a == m_aux.get()) { | ||||||
|  |                     dirty = true; | ||||||
|  |                 } else if (is_app(arg) && cache.find(to_app(arg), r)) { | ||||||
|  |                     dirty |= (arg != r); | ||||||
|  |                     args.push_back(r); | ||||||
|  |                 } else if (is_app(arg)) | ||||||
|  |                 { todo.push_back(to_app(arg)); } | ||||||
|  |                 else | ||||||
|  |                     // -- not an app
 | ||||||
|  |                 { args.push_back(arg); } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |             if (todo_sz < todo.size()) { | ||||||
|  |                 // -- process parents
 | ||||||
|  |                 args.reset(); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // ready to re-create
 | ||||||
|  |             app_ref newp(m); | ||||||
|  |             if (!dirty) { newp = p; } | ||||||
|  |             else if (m.is_unit_resolution(p)) { | ||||||
|  |                 if (args.size() == 2) | ||||||
|  |                     // unit resolution with m_aux that got collapsed to nothing
 | ||||||
|  |                 { newp = to_app(args.get(0)); } | ||||||
|  |                 else { | ||||||
|  |                     ptr_vector<proof> parents; | ||||||
|  |                     for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) | ||||||
|  |                     { parents.push_back(to_app(args.get(i))); } | ||||||
|  |                     SASSERT(parents.size() == args.size() - 1); | ||||||
|  |                     newp = m.mk_unit_resolution(parents.size(), parents.c_ptr()); | ||||||
|  |                     // XXX the old and new facts should be
 | ||||||
|  |                     // equivalent. The test here is much
 | ||||||
|  |                     // stronger. It might need to be relaxed.
 | ||||||
|  |                     SASSERT(m.get_fact(newp) == args.back()); | ||||||
|  |                     pinned.push_back(newp); | ||||||
|  |                 } | ||||||
|  |             } else if (matches_fact(args, a)) { | ||||||
|  |                 newp = to_app(a); | ||||||
|  |             } else { | ||||||
|  |                 expr_ref papp(m); | ||||||
|  |                 mk_app(p->get_decl(), args, papp); | ||||||
|  |                 newp = to_app(papp.get()); | ||||||
|  |                 pinned.push_back(newp); | ||||||
|  |             } | ||||||
|  |             cache.insert(p, newp); | ||||||
|  |             todo.pop_back(); | ||||||
|  |             CTRACE("virtual", | ||||||
|  |                    p->get_decl_kind() == PR_TH_LEMMA && | ||||||
|  |                    p->get_decl()->get_parameter(0).get_symbol() == "arith" && | ||||||
|  |                    p->get_decl()->get_num_parameters() > 1 && | ||||||
|  |                    p->get_decl()->get_parameter(1).get_symbol() == "farkas", | ||||||
|  |                    tout << "Old pf: " << mk_pp(p, m) << "\n" | ||||||
|  |                    << "New pf: " << mk_pp(newp, m) << "\n";); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         proof *r; | ||||||
|  |         VERIFY(cache.find(pr, r)); | ||||||
|  | 
 | ||||||
|  |         DEBUG_CODE( | ||||||
|  |             proof_checker pc(m); | ||||||
|  |             expr_ref_vector side(m); | ||||||
|  |             SASSERT(pc.check(r, side)); | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         res = r ; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | proof *virtual_solver::get_proof() | ||||||
|  | { | ||||||
|  |     scoped_watch _t_(m_factory.m_proof_watch); | ||||||
|  | 
 | ||||||
|  |     if (!m_proof.get()) { | ||||||
|  |         elim_aux_assertions pc(m_pred); | ||||||
|  |         m_proof = m_context.get_proof(); | ||||||
|  |         pc(m, m_proof.get(), m_proof); | ||||||
|  |     } | ||||||
|  |     return m_proof.get(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool virtual_solver::is_aux_predicate(expr *p) | ||||||
|  | {return is_app(p) && to_app(p) == m_pred.get();} | ||||||
|  | 
 | ||||||
|  | lbool virtual_solver::check_sat_core(unsigned num_assumptions, | ||||||
|  |                                      expr *const * assumptions) | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed || get_scope_level() > 0); | ||||||
|  |     m_proof.reset(); | ||||||
|  |     scoped_watch _t_(m_factory.m_check_watch); | ||||||
|  |     m_factory.m_stats.m_num_smt_checks++; | ||||||
|  | 
 | ||||||
|  |     stopwatch sw; | ||||||
|  |     sw.start(); | ||||||
|  |     internalize_assertions(); | ||||||
|  |     if (false) { | ||||||
|  |         std::stringstream file_name; | ||||||
|  |         file_name << "virt_solver"; | ||||||
|  |         if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } | ||||||
|  |         file_name << "_" << (m_dump_counter++) << ".smt2"; | ||||||
|  | 
 | ||||||
|  |         verbose_stream() << "Dumping SMT2 benchmark: " << file_name.str() << "\n"; | ||||||
|  | 
 | ||||||
|  |         std::ofstream out(file_name.str().c_str()); | ||||||
|  | 
 | ||||||
|  |         to_smt2_benchmark(out, m_context, num_assumptions, assumptions, | ||||||
|  |                           "virt_solver"); | ||||||
|  | 
 | ||||||
|  |         out << "(exit)\n"; | ||||||
|  |         out.close(); | ||||||
|  |     } | ||||||
|  |     lbool res = m_context.check(num_assumptions, assumptions); | ||||||
|  |     sw.stop(); | ||||||
|  |     if (res == l_true) { | ||||||
|  |         m_factory.m_check_sat_watch.add(sw); | ||||||
|  |         m_factory.m_stats.m_num_sat_smt_checks++; | ||||||
|  |     } else if (res == l_undef) { | ||||||
|  |         m_factory.m_check_undef_watch.add(sw); | ||||||
|  |         m_factory.m_stats.m_num_undef_smt_checks++; | ||||||
|  |     } | ||||||
|  |     set_status(res); | ||||||
|  | 
 | ||||||
|  |     if (m_dump_benchmarks && | ||||||
|  |             sw.get_seconds() >= m_factory.fparams().m_dump_min_time) { | ||||||
|  |         std::stringstream file_name; | ||||||
|  |         file_name << "virt_solver"; | ||||||
|  |         if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } | ||||||
|  |         file_name << "_" << (m_dump_counter++) << ".smt2"; | ||||||
|  | 
 | ||||||
|  |         std::ofstream out(file_name.str().c_str()); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         out << "(set-info :status "; | ||||||
|  |         if (res == l_true) { out << "sat"; } | ||||||
|  |         else if (res == l_false) { out << "unsat"; } | ||||||
|  |         else { out << "unknown"; } | ||||||
|  |         out << ")\n"; | ||||||
|  | 
 | ||||||
|  |         to_smt2_benchmark(out, m_context, num_assumptions, assumptions, | ||||||
|  |                           "virt_solver"); | ||||||
|  | 
 | ||||||
|  |         out << "(exit)\n"; | ||||||
|  |         ::statistics st; | ||||||
|  |         m_context.collect_statistics(st); | ||||||
|  |         st.update("time", sw.get_seconds()); | ||||||
|  |         st.display_smt2(out); | ||||||
|  | 
 | ||||||
|  |         out.close(); | ||||||
|  | 
 | ||||||
|  |         if (m_factory.fparams().m_dump_recheck) { | ||||||
|  |             scoped_no_proof _no_proof_(m); | ||||||
|  |             smt_params p; | ||||||
|  |             stopwatch sw2; | ||||||
|  |             smt::kernel kernel(m, p); | ||||||
|  |             for (unsigned i = 0, sz = m_context.size(); i < sz; ++i) | ||||||
|  |             { kernel.assert_expr(m_context.get_formulas()[i]); } | ||||||
|  |             sw2.start(); | ||||||
|  |             kernel.check(num_assumptions, assumptions); | ||||||
|  |             sw2.stop(); | ||||||
|  |             verbose_stream() << file_name.str() << " :orig " | ||||||
|  |                              << sw.get_seconds() << " :new " << sw2.get_seconds(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver::push_core() | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed || get_scope_level() > 0); | ||||||
|  |     if (m_in_delay_scope) { | ||||||
|  |         // second push
 | ||||||
|  |         internalize_assertions(); | ||||||
|  |         m_context.push(); | ||||||
|  |         m_pushed = true; | ||||||
|  |         m_in_delay_scope = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!m_pushed) { m_in_delay_scope = true; } | ||||||
|  |     else { | ||||||
|  |         SASSERT(m_pushed); | ||||||
|  |         SASSERT(!m_in_delay_scope); | ||||||
|  |         m_context.push(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void virtual_solver::pop_core(unsigned n) | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed || get_scope_level() > 0); | ||||||
|  |     if (m_pushed) { | ||||||
|  |         SASSERT(!m_in_delay_scope); | ||||||
|  |         m_context.pop(n); | ||||||
|  |         m_pushed = get_scope_level() - n > 0; | ||||||
|  |     } else | ||||||
|  |     { m_in_delay_scope = get_scope_level() - n > 0; } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver::get_unsat_core(ptr_vector<expr> &r) | ||||||
|  | { | ||||||
|  |     for (unsigned i = 0, sz = m_context.get_unsat_core_size(); i < sz; ++i) { | ||||||
|  |         expr *core = m_context.get_unsat_core_expr(i); | ||||||
|  |         if (is_aux_predicate(core)) { continue; } | ||||||
|  |         r.push_back(core); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver::assert_expr(expr *e) | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed || get_scope_level() > 0); | ||||||
|  |     if (m.is_true(e)) { return; } | ||||||
|  |     if (m_in_delay_scope) { | ||||||
|  |         internalize_assertions(); | ||||||
|  |         m_context.push(); | ||||||
|  |         m_pushed = true; | ||||||
|  |         m_in_delay_scope = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (m_pushed) | ||||||
|  |     { m_context.assert_expr(e); } | ||||||
|  |     else { | ||||||
|  |         m_flat.push_back(e); | ||||||
|  |         flatten_and(m_flat); | ||||||
|  |         m_assertions.append(m_flat); | ||||||
|  |         m_flat.reset(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void virtual_solver::internalize_assertions() | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed || m_head == m_assertions.size()); | ||||||
|  |     for (unsigned sz = m_assertions.size(); m_head < sz; ++m_head) { | ||||||
|  |         expr_ref f(m); | ||||||
|  |         f = m.mk_implies(m_pred, (m_assertions.get(m_head))); | ||||||
|  |         m_context.assert_expr(f); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | void virtual_solver::refresh() | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed); | ||||||
|  |     m_head = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver::reset() | ||||||
|  | { | ||||||
|  |     SASSERT(!m_pushed); | ||||||
|  |     m_head = 0; | ||||||
|  |     m_assertions.reset(); | ||||||
|  |     m_factory.refresh(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver::get_labels(svector<symbol> &r) | ||||||
|  | { | ||||||
|  |     r.reset(); | ||||||
|  |     buffer<symbol> tmp; | ||||||
|  |     m_context.get_relevant_labels(0, tmp); | ||||||
|  |     r.append(tmp.size(), tmp.c_ptr()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | solver* virtual_solver::translate(ast_manager& m, params_ref const& p) | ||||||
|  | { | ||||||
|  |     UNREACHABLE(); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | void virtual_solver::updt_params(params_ref const &p) | ||||||
|  | { m_factory.updt_params(p); } | ||||||
|  | void virtual_solver::collect_param_descrs(param_descrs &r) | ||||||
|  | { m_factory.collect_param_descrs(r); } | ||||||
|  | void virtual_solver::set_produce_models(bool f) | ||||||
|  | { m_factory.set_produce_models(f); } | ||||||
|  | bool virtual_solver::get_produce_models() | ||||||
|  | {return m_factory.get_produce_models(); } | ||||||
|  | smt_params &virtual_solver::fparams() | ||||||
|  | {return m_factory.fparams();} | ||||||
|  | 
 | ||||||
|  | void virtual_solver::to_smt2_benchmark(std::ostream &out, | ||||||
|  |                                        smt::kernel &context, | ||||||
|  |                                        unsigned num_assumptions, | ||||||
|  |                                        expr * const * assumptions, | ||||||
|  |                                        char const * name, | ||||||
|  |                                        symbol const &logic, | ||||||
|  |                                        char const * status, | ||||||
|  |                                        char const * attributes) | ||||||
|  | { | ||||||
|  |     ast_pp_util pp(m); | ||||||
|  |     expr_ref_vector asserts(m); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = 0, sz = context.size(); i < sz; ++i) { | ||||||
|  |         asserts.push_back(context.get_formulas()[i]); | ||||||
|  |         pp.collect(asserts.back()); | ||||||
|  |     } | ||||||
|  |     pp.collect(num_assumptions, assumptions); | ||||||
|  |     pp.display_decls(out); | ||||||
|  |     pp.display_asserts(out, asserts); | ||||||
|  |     out << "(check-sat "; | ||||||
|  |     for (unsigned i = 0; i < num_assumptions; ++i) | ||||||
|  |     { out << mk_pp(assumptions[i], m) << " "; } | ||||||
|  |     out << ")\n"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | virtual_solver_factory::virtual_solver_factory(ast_manager &mgr, smt_params &fparams) : | ||||||
|  |     m_fparams(fparams), m(mgr), m_context(m, m_fparams) | ||||||
|  | { | ||||||
|  |     m_stats.reset(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | virtual_solver* virtual_solver_factory::mk_solver() | ||||||
|  | { | ||||||
|  |     std::stringstream name; | ||||||
|  |     name << "vsolver#" << m_solvers.size(); | ||||||
|  |     app_ref pred(m); | ||||||
|  |     pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); | ||||||
|  |     SASSERT(m_context.get_scope_level() == 0); | ||||||
|  |     m_solvers.push_back(alloc(virtual_solver, *this, m_context, pred)); | ||||||
|  |     return m_solvers.back(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver_factory::collect_statistics(statistics &st) const | ||||||
|  | { | ||||||
|  |     m_context.collect_statistics(st); | ||||||
|  |     st.update("time.virtual_solver.smt.total", m_check_watch.get_seconds()); | ||||||
|  |     st.update("time.virtual_solver.smt.total.sat", m_check_sat_watch.get_seconds()); | ||||||
|  |     st.update("time.virtual_solver.smt.total.undef", m_check_undef_watch.get_seconds()); | ||||||
|  |     st.update("time.virtual_solver.proof", m_proof_watch.get_seconds()); | ||||||
|  |     st.update("virtual_solver.checks", m_stats.m_num_smt_checks); | ||||||
|  |     st.update("virtual_solver.checks.sat", m_stats.m_num_sat_smt_checks); | ||||||
|  |     st.update("virtual_solver.checks.undef", m_stats.m_num_undef_smt_checks); | ||||||
|  | } | ||||||
|  | void virtual_solver_factory::reset_statistics() | ||||||
|  | { | ||||||
|  |     m_context.reset_statistics(); | ||||||
|  |     m_stats.reset(); | ||||||
|  |     m_check_sat_watch.reset(); | ||||||
|  |     m_check_undef_watch.reset(); | ||||||
|  |     m_check_watch.reset(); | ||||||
|  |     m_proof_watch.reset(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void virtual_solver_factory::refresh() | ||||||
|  | { | ||||||
|  |     m_context.reset(); | ||||||
|  |     for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) | ||||||
|  |     { m_solvers [i]->refresh(); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | virtual_solver_factory::~virtual_solver_factory() | ||||||
|  | { | ||||||
|  |     for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) | ||||||
|  |     { dealloc(m_solvers [i]); } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										153
									
								
								src/muz/spacer/spacer_virtual_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/muz/spacer/spacer_virtual_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,153 @@ | ||||||
|  | /**
 | ||||||
|  | Copyright (c) 2017 Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Module Name: | ||||||
|  | 
 | ||||||
|  |     spacer_virtual_solver.h | ||||||
|  | 
 | ||||||
|  | Abstract: | ||||||
|  | 
 | ||||||
|  |    multi-solver view of a single smt::kernel | ||||||
|  | 
 | ||||||
|  | Author: | ||||||
|  | 
 | ||||||
|  |     Arie Gurfinkel | ||||||
|  | 
 | ||||||
|  | Notes: | ||||||
|  | 
 | ||||||
|  | --*/ | ||||||
|  | #ifndef SPACER_VIRTUAL_SOLVER_H_ | ||||||
|  | #define SPACER_VIRTUAL_SOLVER_H_ | ||||||
|  | #include"ast.h" | ||||||
|  | #include"params.h" | ||||||
|  | #include"solver_na2as.h" | ||||||
|  | #include"smt_kernel.h" | ||||||
|  | #include"smt_params.h" | ||||||
|  | #include"stopwatch.h" | ||||||
|  | namespace spacer { | ||||||
|  | class virtual_solver_factory; | ||||||
|  | 
 | ||||||
|  | class virtual_solver : public solver_na2as { | ||||||
|  |     friend class virtual_solver_factory; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     virtual_solver_factory &m_factory; | ||||||
|  |     ast_manager &m; | ||||||
|  |     smt::kernel &m_context; | ||||||
|  |     app_ref m_pred; | ||||||
|  | 
 | ||||||
|  |     bool m_virtual; | ||||||
|  |     expr_ref_vector m_assertions; | ||||||
|  |     unsigned m_head; | ||||||
|  |     // temporary to flatten conjunction
 | ||||||
|  |     expr_ref_vector m_flat; | ||||||
|  | 
 | ||||||
|  |     bool m_pushed; | ||||||
|  |     bool m_in_delay_scope; | ||||||
|  |     bool m_dump_benchmarks; | ||||||
|  |     unsigned m_dump_counter; | ||||||
|  | 
 | ||||||
|  |     proof_ref m_proof; | ||||||
|  | 
 | ||||||
|  |     virtual_solver(virtual_solver_factory &factory, smt::kernel &context, app* pred); | ||||||
|  | 
 | ||||||
|  |     bool is_aux_predicate(expr *p); | ||||||
|  |     void internalize_assertions(); | ||||||
|  |     void to_smt2_benchmark(std::ostream &out, | ||||||
|  |                            smt::kernel &context, | ||||||
|  |                            unsigned num_assumptions, | ||||||
|  |                            expr * const * assumptions, | ||||||
|  |                            char const * name = "benchmarks", | ||||||
|  |                            symbol const &logic = symbol::null, | ||||||
|  |                            char const * status = "unknown", | ||||||
|  |                            char const * attributes = ""); | ||||||
|  | 
 | ||||||
|  |     void refresh(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     virtual ~virtual_solver(); | ||||||
|  |     virtual unsigned get_num_assumptions() const | ||||||
|  |     { | ||||||
|  |         unsigned sz = solver_na2as::get_num_assumptions(); | ||||||
|  |         return m_virtual ? sz - 1 : sz; | ||||||
|  |     } | ||||||
|  |     virtual expr* get_assumption(unsigned idx) const | ||||||
|  |     { | ||||||
|  |         if(m_virtual) { idx++; } | ||||||
|  |         return solver_na2as::get_assumption(idx); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     virtual void get_unsat_core(ptr_vector<expr> &r); | ||||||
|  |     virtual void assert_expr(expr *e); | ||||||
|  |     virtual void collect_statistics(statistics &st) const {} | ||||||
|  |     virtual void get_model(model_ref &m) {m_context.get_model(m);} | ||||||
|  |     virtual proof* get_proof(); | ||||||
|  |     virtual std::string reason_unknown() const | ||||||
|  |     {return m_context.last_failure_as_string();} | ||||||
|  |     virtual void set_reason_unknown(char const *msg) | ||||||
|  |     {m_context.set_reason_unknown(msg);} | ||||||
|  |     virtual ast_manager& get_manager() const {return m;} | ||||||
|  |     virtual void get_labels(svector<symbol> &r); | ||||||
|  |     virtual void set_produce_models(bool f); | ||||||
|  |     virtual bool get_produce_models(); | ||||||
|  |     virtual smt_params &fparams(); | ||||||
|  |     virtual void reset(); | ||||||
|  | 
 | ||||||
|  |     virtual void set_progress_callback(progress_callback *callback) | ||||||
|  |     {UNREACHABLE();} | ||||||
|  | 
 | ||||||
|  |     virtual solver *translate(ast_manager &m, params_ref const &p); | ||||||
|  | 
 | ||||||
|  |     virtual void updt_params(params_ref const &p); | ||||||
|  |     virtual void collect_param_descrs(param_descrs &r); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     virtual lbool check_sat_core(unsigned num_assumptions, expr *const * assumptions); | ||||||
|  |     virtual void push_core(); | ||||||
|  |     virtual void pop_core(unsigned n); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// multi-solver abstraction on top of a single smt::kernel
 | ||||||
|  | class virtual_solver_factory { | ||||||
|  |     friend class virtual_solver; | ||||||
|  | private: | ||||||
|  |     smt_params  &m_fparams; | ||||||
|  |     ast_manager &m; | ||||||
|  |     smt::kernel m_context; | ||||||
|  |     /// solvers managed by this factory
 | ||||||
|  |     ptr_vector<virtual_solver> m_solvers; | ||||||
|  | 
 | ||||||
|  |     struct stats { | ||||||
|  |         unsigned m_num_smt_checks; | ||||||
|  |         unsigned m_num_sat_smt_checks; | ||||||
|  |         unsigned m_num_undef_smt_checks; | ||||||
|  |         stats() { reset(); } | ||||||
|  |         void reset() { memset(this, 0, sizeof(*this)); } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     stats m_stats; | ||||||
|  |     stopwatch m_check_watch; | ||||||
|  |     stopwatch m_check_sat_watch; | ||||||
|  |     stopwatch m_check_undef_watch; | ||||||
|  |     stopwatch m_proof_watch; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     void refresh(); | ||||||
|  | public: | ||||||
|  |     virtual_solver_factory(ast_manager &mgr, smt_params &fparams); | ||||||
|  |     virtual ~virtual_solver_factory(); | ||||||
|  |     virtual_solver* mk_solver(); | ||||||
|  |     void collect_statistics(statistics &st) const; | ||||||
|  |     void reset_statistics(); | ||||||
|  |     void updt_params(params_ref const &p) { m_fparams.updt_params(p); } | ||||||
|  |     void collect_param_descrs(param_descrs &r) { /* empty */ } | ||||||
|  |     void set_produce_models(bool f) { m_fparams.m_model = f; } | ||||||
|  |     bool get_produce_models() { return m_fparams.m_model; } | ||||||
|  |     smt_params &fparams() { return m_fparams; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue