mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/Z3Prover/z3 into jfleisher/nightlyversion
This commit is contained in:
		
						commit
						9c7a3e4f8d
					
				
					 116 changed files with 7435 additions and 3213 deletions
				
			
		| 
						 | 
					@ -10,6 +10,11 @@ Version 4.next
 | 
				
			||||||
    - native word level bit-vector solving.
 | 
					    - native word level bit-vector solving.
 | 
				
			||||||
  - introduction of simple induction lemmas to handle a limited repertoire of induction proofs.
 | 
					  - introduction of simple induction lemmas to handle a limited repertoire of induction proofs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Version 4.11.1
 | 
				
			||||||
 | 
					==============
 | 
				
			||||||
 | 
					- add error handling to fromString method in JavaScript
 | 
				
			||||||
 | 
					- fix regression in default parameters for CDCL (Nuno Lopes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Version 4.11.0
 | 
					Version 4.11.0
 | 
				
			||||||
==============
 | 
					==============
 | 
				
			||||||
- remove `Z3_bool`, `Z3_TRUE`, `Z3_FALSE` from the API. Use `bool`, `true`, `false` instead.
 | 
					- remove `Z3_bool`, `Z3_TRUE`, `Z3_FALSE` from the API. Use `bool`, `true`, `false` instead.
 | 
				
			||||||
| 
						 | 
					@ -23,7 +28,7 @@ Version 4.11.0
 | 
				
			||||||
  - it allows to apply incremental pre-processing of bit-vectors by identifying ranges that are known to be constant.
 | 
					  - it allows to apply incremental pre-processing of bit-vectors by identifying ranges that are known to be constant.
 | 
				
			||||||
    This rewrite is beneficial, for instance, when bit-vectors are constrained to have many high-level bits set to 0.
 | 
					    This rewrite is beneficial, for instance, when bit-vectors are constrained to have many high-level bits set to 0.
 | 
				
			||||||
- add feature to model-based projection for arithmetic to handle integer division.
 | 
					- add feature to model-based projection for arithmetic to handle integer division.
 | 
				
			||||||
- add from_string method to JavaScript solver object.
 | 
					- add fromString method to JavaScript solver object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Version 4.10.2
 | 
					Version 4.10.2
 | 
				
			||||||
==============
 | 
					==============
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,7 @@ def help(ous):
 | 
				
			||||||
    ous.write("Z3 Options\n")
 | 
					    ous.write("Z3 Options\n")
 | 
				
			||||||
    z3_exe = BUILD_DIR + "/z3"
 | 
					    z3_exe = BUILD_DIR + "/z3"
 | 
				
			||||||
    out = subprocess.Popen([z3_exe, "-pm"],stdout=subprocess.PIPE).communicate()[0]
 | 
					    out = subprocess.Popen([z3_exe, "-pm"],stdout=subprocess.PIPE).communicate()[0]
 | 
				
			||||||
    modules = []
 | 
					    modules = ["global"]
 | 
				
			||||||
    if out != None:
 | 
					    if out != None:
 | 
				
			||||||
        out = out.decode(sys.stdout.encoding)
 | 
					        out = out.decode(sys.stdout.encoding)
 | 
				
			||||||
        module_re = re.compile(r"\[module\] (.*)\,")
 | 
					        module_re = re.compile(r"\[module\] (.*)\,")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ def init_project_def():
 | 
				
			||||||
    add_lib('params', ['util'])
 | 
					    add_lib('params', ['util'])
 | 
				
			||||||
    add_lib('smt_params', ['params'], 'smt/params')
 | 
					    add_lib('smt_params', ['params'], 'smt/params')
 | 
				
			||||||
    add_lib('grobner', ['ast', 'dd', 'simplex'], 'math/grobner')    
 | 
					    add_lib('grobner', ['ast', 'dd', 'simplex'], 'math/grobner')    
 | 
				
			||||||
    add_lib('sat', ['util', 'dd', 'grobner'])    
 | 
					    add_lib('sat', ['params', 'util', 'dd', 'grobner'])    
 | 
				
			||||||
    add_lib('nlsat', ['polynomial', 'sat'])
 | 
					    add_lib('nlsat', ['polynomial', 'sat'])
 | 
				
			||||||
    add_lib('lp', ['util', 'nlsat', 'grobner', 'interval', 'smt_params'], 'math/lp')
 | 
					    add_lib('lp', ['util', 'nlsat', 'grobner', 'interval', 'smt_params'], 'math/lp')
 | 
				
			||||||
    add_lib('rewriter', ['ast', 'polynomial', 'automata', 'params'], 'ast/rewriter')
 | 
					    add_lib('rewriter', ['ast', 'polynomial', 'automata', 'params'], 'ast/rewriter')
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@ def init_project_def():
 | 
				
			||||||
    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_fpa.h', 'z3_spacer.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_fpa.h', 'z3_spacer.h']
 | 
				
			||||||
    add_lib('api', ['portfolio',  'realclosure', 'opt'],
 | 
					    add_lib('api', ['portfolio',  'realclosure', 'opt'],
 | 
				
			||||||
            includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
 | 
					            includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
 | 
				
			||||||
    add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'arith_tactics'], 'cmd_context/extra_cmds')
 | 
					    add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'euf', 'arith_tactics'], 'cmd_context/extra_cmds')
 | 
				
			||||||
    add_exe('shell', ['api', 'sat', 'extra_cmds', 'opt'], exe_name='z3')
 | 
					    add_exe('shell', ['api', 'sat', 'extra_cmds', 'opt'], exe_name='z3')
 | 
				
			||||||
    add_exe('test', ['api', 'fuzzing', 'simplex', 'sat_smt'], exe_name='test-z3', install=False)
 | 
					    add_exe('test', ['api', 'fuzzing', 'simplex', 'sat_smt'], exe_name='test-z3', install=False)
 | 
				
			||||||
    _libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll',
 | 
					    _libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -242,6 +242,7 @@ extern "C" {
 | 
				
			||||||
        std::istringstream is(s);
 | 
					        std::istringstream is(s);
 | 
				
			||||||
        ctx->set_regular_stream(ous);
 | 
					        ctx->set_regular_stream(ous);
 | 
				
			||||||
        ctx->set_diagnostic_stream(ous);
 | 
					        ctx->set_diagnostic_stream(ous);
 | 
				
			||||||
 | 
					        cmd_context::scoped_redirect _redirect(*ctx);
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            if (!parse_smt2_commands(*ctx.get(), is)) {
 | 
					            if (!parse_smt2_commands(*ctx.get(), is)) {
 | 
				
			||||||
                SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str());
 | 
					                SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,6 +71,7 @@ namespace Microsoft.Z3
 | 
				
			||||||
        Solver solver;
 | 
					        Solver solver;
 | 
				
			||||||
        Context ctx;
 | 
					        Context ctx;
 | 
				
			||||||
        Z3_solver_callback callback = IntPtr.Zero;
 | 
					        Z3_solver_callback callback = IntPtr.Zero;
 | 
				
			||||||
 | 
					        int callbackNesting = 0;
 | 
				
			||||||
        FixedEh fixed_eh;
 | 
					        FixedEh fixed_eh;
 | 
				
			||||||
        Action final_eh;
 | 
					        Action final_eh;
 | 
				
			||||||
        EqEh eq_eh;
 | 
					        EqEh eq_eh;
 | 
				
			||||||
| 
						 | 
					@ -91,6 +92,7 @@ namespace Microsoft.Z3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void Callback(Action fn, Z3_solver_callback cb)
 | 
					        void Callback(Action fn, Z3_solver_callback cb)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            this.callbackNesting++;
 | 
				
			||||||
            this.callback = cb;
 | 
					            this.callback = cb;
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					@ -102,7 +104,9 @@ namespace Microsoft.Z3
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            finally
 | 
					            finally
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                this.callback = IntPtr.Zero;
 | 
					                callbackNesting--;
 | 
				
			||||||
 | 
					                if (callbackNesting == 0) // callbacks can be nested (e.g., internalizing new element in "created")
 | 
				
			||||||
 | 
					                    this.callback = IntPtr.Zero;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -889,8 +889,10 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void basic_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
 | 
					void basic_decl_plugin::get_sort_names(svector<builtin_name> & sort_names, symbol const & logic) {
 | 
				
			||||||
    if (logic == symbol::null)
 | 
					    if (logic == symbol::null) {
 | 
				
			||||||
        sort_names.push_back(builtin_name("bool", BOOL_SORT));
 | 
					        sort_names.push_back(builtin_name("bool", BOOL_SORT));
 | 
				
			||||||
 | 
					        sort_names.push_back(builtin_name("Proof", PROOF_SORT)); // reserved name?
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    sort_names.push_back(builtin_name("Bool", BOOL_SORT));
 | 
					    sort_names.push_back(builtin_name("Bool", BOOL_SORT));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -129,6 +129,7 @@ void ast_pp_util::push() {
 | 
				
			||||||
    m_rec_decls.push();
 | 
					    m_rec_decls.push();
 | 
				
			||||||
    m_decls.push();
 | 
					    m_decls.push();
 | 
				
			||||||
    m_sorts.push();
 | 
					    m_sorts.push();
 | 
				
			||||||
 | 
					    m_defined_lim.push_back(m_defined.size());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ast_pp_util::pop(unsigned n) {
 | 
					void ast_pp_util::pop(unsigned n) {
 | 
				
			||||||
| 
						 | 
					@ -136,4 +137,55 @@ void ast_pp_util::pop(unsigned n) {
 | 
				
			||||||
    m_rec_decls.pop(n);
 | 
					    m_rec_decls.pop(n);
 | 
				
			||||||
    m_decls.pop(n);
 | 
					    m_decls.pop(n);
 | 
				
			||||||
    m_sorts.pop(n);
 | 
					    m_sorts.pop(n);
 | 
				
			||||||
 | 
					    unsigned old_sz = m_defined_lim[m_defined_lim.size() - n];
 | 
				
			||||||
 | 
					    for (unsigned i = m_defined.size(); i-- > old_sz; ) 
 | 
				
			||||||
 | 
					        m_is_defined.mark(m_defined.get(i), false);
 | 
				
			||||||
 | 
					    m_defined.shrink(old_sz);
 | 
				
			||||||
 | 
					    m_defined_lim.shrink(m_defined_lim.size() - n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& ast_pp_util::display_expr_def(std::ostream& out, expr* n) {
 | 
				
			||||||
 | 
					    if (is_app(n) && to_app(n)->get_num_args() == 0)
 | 
				
			||||||
 | 
					        return out << mk_pp(n, m);
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        return out << "$" << n->get_id();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::ostream& ast_pp_util::define_expr(std::ostream& out, expr* n) {
 | 
				
			||||||
 | 
					    ptr_buffer<expr> visit;
 | 
				
			||||||
 | 
					    visit.push_back(n);
 | 
				
			||||||
 | 
					    while (!visit.empty()) {
 | 
				
			||||||
 | 
					        n = visit.back();
 | 
				
			||||||
 | 
					        if (m_is_defined.is_marked(n)) {
 | 
				
			||||||
 | 
					            visit.pop_back();
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (is_app(n)) {
 | 
				
			||||||
 | 
					            bool all_visit = true;
 | 
				
			||||||
 | 
					            for (auto* e : *to_app(n)) {
 | 
				
			||||||
 | 
					                if (m_is_defined.is_marked(e))
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                all_visit = false;
 | 
				
			||||||
 | 
					                visit.push_back(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!all_visit)
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            m_defined.push_back(n);
 | 
				
			||||||
 | 
					            m_is_defined.mark(n, true);
 | 
				
			||||||
 | 
					            visit.pop_back();
 | 
				
			||||||
 | 
					            if (to_app(n)->get_num_args() > 0) {
 | 
				
			||||||
 | 
					                out << "(define-const $" << n->get_id() << " " << mk_pp(n->get_sort(), m) << " (";            
 | 
				
			||||||
 | 
					                out << mk_ismt2_func(to_app(n)->get_decl(), m);
 | 
				
			||||||
 | 
					                for (auto* e : *to_app(n)) 
 | 
				
			||||||
 | 
					                    display_expr_def(out << " ", e);
 | 
				
			||||||
 | 
					                out << "))\n";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        out << "(define-const $" << n->get_id() << " " << mk_pp(n->get_sort(), m) << " " << mk_pp(n, m) << ")\n";                
 | 
				
			||||||
 | 
					        m_defined.push_back(n);
 | 
				
			||||||
 | 
					        m_is_defined.mark(n, true);
 | 
				
			||||||
 | 
					        visit.pop_back();        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,15 +30,18 @@ class ast_pp_util {
 | 
				
			||||||
    stacked_value<unsigned> m_rec_decls;
 | 
					    stacked_value<unsigned> m_rec_decls;
 | 
				
			||||||
    stacked_value<unsigned> m_decls;
 | 
					    stacked_value<unsigned> m_decls;
 | 
				
			||||||
    stacked_value<unsigned> m_sorts;
 | 
					    stacked_value<unsigned> m_sorts;
 | 
				
			||||||
 | 
					    expr_mark               m_is_defined;
 | 
				
			||||||
 | 
					    expr_ref_vector         m_defined;
 | 
				
			||||||
 | 
					    unsigned_vector         m_defined_lim;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    decl_collector      coll;
 | 
					    decl_collector      coll;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    ast_pp_util(ast_manager& m): m(m), m_env(m), m_rec_decls(0), m_decls(0), m_sorts(0), m_defined(m), coll(m) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ast_pp_util(ast_manager& m): m(m), m_env(m), m_rec_decls(0), m_decls(0), m_sorts(0), coll(m) {}
 | 
					    void reset() { coll.reset(); m_removed.reset(); m_sorts.clear(0u); m_decls.clear(0u); m_rec_decls.clear(0u); 
 | 
				
			||||||
 | 
					        m_is_defined.reset(); m_defined.reset(); m_defined_lim.reset(); }
 | 
				
			||||||
    void reset() { coll.reset(); m_removed.reset(); m_sorts.clear(0u); m_decls.clear(0u); m_rec_decls.clear(0u); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void collect(expr* e);
 | 
					    void collect(expr* e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +63,10 @@ class ast_pp_util {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::ostream& display_expr(std::ostream& out, expr* f, bool neat = true);
 | 
					    std::ostream& display_expr(std::ostream& out, expr* f, bool neat = true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ostream& define_expr(std::ostream& out, expr* f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ostream& display_expr_def(std::ostream& out, expr* f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void push();
 | 
					    void push();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void pop(unsigned n);
 | 
					    void pop(unsigned n);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -620,7 +620,7 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
 | 
				
			||||||
        for (unsigned i = 0; i < num_args; ++i) {
 | 
					        for (unsigned i = 0; i < num_args; ++i) {
 | 
				
			||||||
            if (args[i]->get_sort() != r->get_domain(i)) {
 | 
					            if (args[i]->get_sort() != r->get_domain(i)) {
 | 
				
			||||||
                std::ostringstream buffer;
 | 
					                std::ostringstream buffer;
 | 
				
			||||||
                buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " does not match declaration " << mk_pp(r, m);
 | 
					                buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " has sort " << mk_pp(args[i]->get_sort(), m) << " it does does not match declaration " << mk_pp(r, m);
 | 
				
			||||||
                m.raise_exception(buffer.str());
 | 
					                m.raise_exception(buffer.str());
 | 
				
			||||||
                return nullptr;
 | 
					                return nullptr;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ Revision History:
 | 
				
			||||||
   \brief Macros are universally quantified formulas of the form:
 | 
					   \brief Macros are universally quantified formulas of the form:
 | 
				
			||||||
     (forall X  (= (f X) T[X]))
 | 
					     (forall X  (= (f X) T[X]))
 | 
				
			||||||
     (forall X  (iff (f X) T[X]))
 | 
					     (forall X  (iff (f X) T[X]))
 | 
				
			||||||
   where T[X] does not contain X.
 | 
					   where T[X] does not contain f.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   This class is responsible for storing macros and expanding them.
 | 
					   This class is responsible for storing macros and expanding them.
 | 
				
			||||||
   It has support for backtracking and tagging declarations in an expression as forbidded for being macros.
 | 
					   It has support for backtracking and tagging declarations in an expression as forbidded for being macros.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,7 +66,6 @@ void pp(std::ostream & out, format * f, ast_manager & m, params_ref const & _p)
 | 
				
			||||||
    bool     single_line   = p.single_line();
 | 
					    bool     single_line   = p.single_line();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unsigned pos = 0;
 | 
					    unsigned pos = 0;
 | 
				
			||||||
    unsigned ribbon_pos = 0;
 | 
					 | 
				
			||||||
    unsigned line = 0;
 | 
					    unsigned line = 0;
 | 
				
			||||||
    unsigned len;
 | 
					    unsigned len;
 | 
				
			||||||
    unsigned i;
 | 
					    unsigned i;
 | 
				
			||||||
| 
						 | 
					@ -92,7 +91,6 @@ void pp(std::ostream & out, format * f, ast_manager & m, params_ref const & _p)
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            pos += len;
 | 
					            pos += len;
 | 
				
			||||||
            ribbon_pos += len;
 | 
					 | 
				
			||||||
            out << f->get_decl()->get_parameter(0).get_symbol();
 | 
					            out << f->get_decl()->get_parameter(0).get_symbol();
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case OP_INDENT:
 | 
					        case OP_INDENT:
 | 
				
			||||||
| 
						 | 
					@ -121,7 +119,6 @@ void pp(std::ostream & out, format * f, ast_manager & m, params_ref const & _p)
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            pos = indent;
 | 
					            pos = indent;
 | 
				
			||||||
            ribbon_pos = 0;
 | 
					 | 
				
			||||||
            line++;
 | 
					            line++;
 | 
				
			||||||
            if (line < max_num_lines) {
 | 
					            if (line < max_num_lines) {
 | 
				
			||||||
                out << "\n";
 | 
					                out << "\n";
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -291,7 +291,11 @@ namespace recfun {
 | 
				
			||||||
                    expr * e = stack.back();
 | 
					                    expr * e = stack.back();
 | 
				
			||||||
                    stack.pop_back();                    
 | 
					                    stack.pop_back();                    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (m.is_ite(e)) {
 | 
					                    expr* cond = nullptr, *th = nullptr, *el = nullptr; 
 | 
				
			||||||
 | 
					                    if (m.is_ite(e, cond, th, el) && contains_def(u, cond)) {
 | 
				
			||||||
 | 
					                        // skip
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else if (m.is_ite(e)) {
 | 
				
			||||||
                        // need to do a case split on `e`, forking the search space
 | 
					                        // need to do a case split on `e`, forking the search space
 | 
				
			||||||
                        b.to_split = st.cons_ite(to_app(e), b.to_split);
 | 
					                        b.to_split = st.cons_ite(to_app(e), b.to_split);
 | 
				
			||||||
                    } 
 | 
					                    } 
 | 
				
			||||||
| 
						 | 
					@ -338,9 +342,8 @@ namespace recfun {
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // substitute, to get rid of `ite` terms
 | 
					                // substitute, to get rid of `ite` terms
 | 
				
			||||||
                expr_ref case_rhs = subst(rhs);
 | 
					                expr_ref case_rhs = subst(rhs);
 | 
				
			||||||
                for (unsigned i = 0; i < conditions.size(); ++i) {
 | 
					                for (unsigned i = 0; i < conditions.size(); ++i) 
 | 
				
			||||||
                    conditions[i] = subst(conditions.get(i));
 | 
					                    conditions[i] = subst(conditions.get(i));
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // yield new case
 | 
					                // yield new case
 | 
				
			||||||
                bool is_imm = is_i(case_rhs);
 | 
					                bool is_imm = is_i(case_rhs);
 | 
				
			||||||
| 
						 | 
					@ -471,9 +474,8 @@ namespace recfun {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        void plugin::set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) {
 | 
					        void plugin::set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs) {
 | 
				
			||||||
            u().set_definition(r, d, is_macro, n_vars, vars, rhs);
 | 
					            u().set_definition(r, d, is_macro, n_vars, vars, rhs);
 | 
				
			||||||
            for (case_def & c : d.get_def()->get_cases()) {
 | 
					            for (case_def & c : d.get_def()->get_cases()) 
 | 
				
			||||||
                m_case_defs.insert(c.get_decl(), &c);
 | 
					                m_case_defs.insert(c.get_decl(), &c);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool plugin::has_defs() const {
 | 
					        bool plugin::has_defs() const {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,8 +29,8 @@ public:
 | 
				
			||||||
        m.toggle_proof_mode(mode);
 | 
					        m.toggle_proof_mode(mode);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    ~scoped_proof_mode() {
 | 
					    ~scoped_proof_mode() {
 | 
				
			||||||
            m.toggle_proof_mode(m_mode);            
 | 
					        m.toggle_proof_mode(m_mode);            
 | 
				
			||||||
        }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,7 +152,7 @@ public:
 | 
				
			||||||
        return find(to_var(v.get_expr()), v.get_offset(), r);
 | 
					        return find(to_var(v.get_expr()), v.get_offset(), r);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) {
 | 
					    void get_binding(unsigned binding_num, var_offset& var, expr_offset& r) const {
 | 
				
			||||||
        var = m_vars[binding_num];
 | 
					        var = m_vars[binding_num];
 | 
				
			||||||
        VERIFY(m_subst.find(var.first, var.second, r));
 | 
					        VERIFY(m_subst.find(var.first, var.second, r));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -561,6 +561,7 @@ cmd_context::~cmd_context() {
 | 
				
			||||||
    finalize_cmds();
 | 
					    finalize_cmds();
 | 
				
			||||||
    finalize_tactic_cmds();
 | 
					    finalize_tactic_cmds();
 | 
				
			||||||
    finalize_probes();
 | 
					    finalize_probes();
 | 
				
			||||||
 | 
					    m_proof_cmds = nullptr;
 | 
				
			||||||
    reset(true);
 | 
					    reset(true);
 | 
				
			||||||
    m_mcs.reset();
 | 
					    m_mcs.reset();
 | 
				
			||||||
    m_solver = nullptr;
 | 
					    m_solver = nullptr;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,6 +90,17 @@ public:
 | 
				
			||||||
    vector<macro_decl>::iterator end() const { return m_decls->end(); }
 | 
					    vector<macro_decl>::iterator end() const { return m_decls->end(); }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class proof_cmds {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    virtual ~proof_cmds() {}
 | 
				
			||||||
 | 
					    virtual void add_literal(expr* e) = 0;
 | 
				
			||||||
 | 
					    virtual void end_assumption() = 0;
 | 
				
			||||||
 | 
					    virtual void end_learned() = 0;
 | 
				
			||||||
 | 
					    virtual void end_deleted() = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
   \brief Generic wrapper.
 | 
					   \brief Generic wrapper.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
| 
						 | 
					@ -172,6 +183,7 @@ public:
 | 
				
			||||||
    bool owns_manager() const { return m_manager != nullptr; }
 | 
					    bool owns_manager() const { return m_manager != nullptr; }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context {
 | 
					class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    enum status {
 | 
					    enum status {
 | 
				
			||||||
| 
						 | 
					@ -191,6 +203,22 @@ public:
 | 
				
			||||||
        ~scoped_watch() { m_ctx.m_watch.stop(); }
 | 
					        ~scoped_watch() { m_ctx.m_watch.stop(); }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct scoped_redirect {
 | 
				
			||||||
 | 
					        cmd_context& m_ctx;
 | 
				
			||||||
 | 
					        std::ostream& m_verbose;
 | 
				
			||||||
 | 
					        std::ostream* m_warning;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scoped_redirect(cmd_context& ctx): m_ctx(ctx), m_verbose(verbose_stream()), m_warning(warning_stream()) {
 | 
				
			||||||
 | 
					            set_warning_stream(&(*m_ctx.m_diagnostic));
 | 
				
			||||||
 | 
					            set_verbose_stream(m_ctx.diagnostic_stream());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ~scoped_redirect() {
 | 
				
			||||||
 | 
					            set_verbose_stream(m_verbose);
 | 
				
			||||||
 | 
					            set_warning_stream(m_warning);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
| 
						 | 
					@ -209,6 +237,7 @@ protected:
 | 
				
			||||||
    bool                         m_ignore_check = false;      // used by the API to disable check-sat() commands when parsing SMT 2.0 files.
 | 
					    bool                         m_ignore_check = false;      // used by the API to disable check-sat() commands when parsing SMT 2.0 files.
 | 
				
			||||||
    bool                         m_exit_on_error = false;
 | 
					    bool                         m_exit_on_error = false;
 | 
				
			||||||
    bool                         m_allow_duplicate_declarations = false;
 | 
					    bool                         m_allow_duplicate_declarations = false;
 | 
				
			||||||
 | 
					    scoped_ptr<proof_cmds>       m_proof_cmds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static std::ostringstream    g_error_stream;
 | 
					    static std::ostringstream    g_error_stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -381,6 +410,9 @@ public:
 | 
				
			||||||
    pdecl_manager & pm() const { if (!m_pmanager) const_cast<cmd_context*>(this)->init_manager(); return *m_pmanager; }
 | 
					    pdecl_manager & pm() const { if (!m_pmanager) const_cast<cmd_context*>(this)->init_manager(); return *m_pmanager; }
 | 
				
			||||||
    sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast<cmd_context*>(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; }
 | 
					    sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast<cmd_context*>(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proof_cmds* get_proof_cmds() { return m_proof_cmds.get(); }
 | 
				
			||||||
 | 
					    void set_proof_cmds(proof_cmds* pc) { m_proof_cmds = pc; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void set_solver_factory(solver_factory * s);
 | 
					    void set_solver_factory(solver_factory * s);
 | 
				
			||||||
    void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; }
 | 
					    void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; }
 | 
				
			||||||
    check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); }
 | 
					    check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ z3_add_component(extra_cmds
 | 
				
			||||||
    dbg_cmds.cpp
 | 
					    dbg_cmds.cpp
 | 
				
			||||||
    polynomial_cmds.cpp
 | 
					    polynomial_cmds.cpp
 | 
				
			||||||
    subpaving_cmds.cpp
 | 
					    subpaving_cmds.cpp
 | 
				
			||||||
 | 
					    proof_cmds.cpp
 | 
				
			||||||
  COMPONENT_DEPENDENCIES
 | 
					  COMPONENT_DEPENDENCIES
 | 
				
			||||||
    arith_tactics
 | 
					    arith_tactics
 | 
				
			||||||
    cmd_context
 | 
					    cmd_context
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										267
									
								
								src/cmd_context/extra_cmds/proof_cmds.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								src/cmd_context/extra_cmds/proof_cmds.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,267 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2022 Microsoft Corporation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proof_cmds.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Commands for reading and checking proofs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Nikolaj Bjorner (nbjorner) 2022-8-26
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Proof checker for clauses created during search.
 | 
				
			||||||
 | 
					1. Clauses annotated by RUP (reverse unit propagation)
 | 
				
			||||||
 | 
					   are checked to be inferrable using reverse unit propagation
 | 
				
			||||||
 | 
					   based on previous clauses.
 | 
				
			||||||
 | 
					2. Clauses annotated by supported proof rules (proof hints)
 | 
				
			||||||
 | 
					   are checked by custom proof checkers. There is a proof checker
 | 
				
			||||||
 | 
					   for each proof rule. Main proof checkers just have a single step
 | 
				
			||||||
 | 
					   but the framework allows to compose proof rules, each inference
 | 
				
			||||||
 | 
					   is checked for correctness by a plugin. 
 | 
				
			||||||
 | 
					3. When there are no supported plugin to justify the derived
 | 
				
			||||||
 | 
					   clause, or a custom check fails, the fallback is to check that the
 | 
				
			||||||
 | 
					   derived clause is a consequence of the input clauses using SMT.
 | 
				
			||||||
 | 
					   The last approach is a bail-out and offers a weaker notion of
 | 
				
			||||||
 | 
					   self-validation. It is often (but not always) sufficient for using proof
 | 
				
			||||||
 | 
					   checking for debugging, as the root-cause for an unsound inference in z3
 | 
				
			||||||
 | 
					   does not necessarily manifest when checking the conclusion of the
 | 
				
			||||||
 | 
					   inference. An external proof checker that uses such fallbacks could
 | 
				
			||||||
 | 
					   use several solvers, or bootstrap from a solver that can generate certificates
 | 
				
			||||||
 | 
					   when z3 does not.
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "util/small_object_allocator.h"
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include "smt/smt_solver.h"
 | 
				
			||||||
 | 
					#include "sat/sat_solver.h"
 | 
				
			||||||
 | 
					#include "sat/sat_drat.h"
 | 
				
			||||||
 | 
					#include "sat/smt/euf_proof_checker.h"
 | 
				
			||||||
 | 
					#include "cmd_context/cmd_context.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class smt_checker {
 | 
				
			||||||
 | 
					    ast_manager& m;
 | 
				
			||||||
 | 
					    params_ref   m_params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // for checking proof rules (hints)
 | 
				
			||||||
 | 
					    euf::proof_checker m_checker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // for fallback SMT checker
 | 
				
			||||||
 | 
					    scoped_ptr<solver> m_solver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // for RUP
 | 
				
			||||||
 | 
					    symbol       m_rup;
 | 
				
			||||||
 | 
					    sat::solver  m_sat_solver;
 | 
				
			||||||
 | 
					    sat::drat    m_drat;
 | 
				
			||||||
 | 
					    sat::literal_vector m_units;
 | 
				
			||||||
 | 
					    sat::literal_vector m_clause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void add_units() {
 | 
				
			||||||
 | 
					        auto const& units = m_drat.units();
 | 
				
			||||||
 | 
					        for (unsigned i = m_units.size(); i < units.size(); ++i)
 | 
				
			||||||
 | 
					            m_units.push_back(units[i].first);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    smt_checker(ast_manager& m):
 | 
				
			||||||
 | 
					        m(m),
 | 
				
			||||||
 | 
					        m_checker(m),
 | 
				
			||||||
 | 
					        m_sat_solver(m_params, m.limit()), 
 | 
				
			||||||
 | 
					        m_drat(m_sat_solver) 
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        m_params.set_bool("drat.check_unsat", true);
 | 
				
			||||||
 | 
					        m_sat_solver.updt_params(m_params);
 | 
				
			||||||
 | 
					        m_drat.updt_config();
 | 
				
			||||||
 | 
					        m_solver = mk_smt_solver(m, m_params, symbol());
 | 
				
			||||||
 | 
					        m_rup = symbol("rup");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool is_rup(app* proof_hint) {
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					            proof_hint &&
 | 
				
			||||||
 | 
					            proof_hint->get_name() == m_rup;        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void mk_clause(expr_ref_vector const& clause) {
 | 
				
			||||||
 | 
					        m_clause.reset();
 | 
				
			||||||
 | 
					        for (expr* e : clause) {
 | 
				
			||||||
 | 
					            bool sign = false;
 | 
				
			||||||
 | 
					            while (m.is_not(e, e))
 | 
				
			||||||
 | 
					                sign = !sign;
 | 
				
			||||||
 | 
					            m_clause.push_back(sat::literal(e->get_id(), sign));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void mk_clause(expr* e) {
 | 
				
			||||||
 | 
					        m_clause.reset();
 | 
				
			||||||
 | 
					        bool sign = false;
 | 
				
			||||||
 | 
					        while (m.is_not(e, e))
 | 
				
			||||||
 | 
					            sign = !sign;
 | 
				
			||||||
 | 
					        m_clause.push_back(sat::literal(e->get_id(), sign));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    bool check_rup(expr_ref_vector const& clause) {
 | 
				
			||||||
 | 
					        add_units();
 | 
				
			||||||
 | 
					        mk_clause(clause);
 | 
				
			||||||
 | 
					        return m_drat.is_drup(m_clause.size(), m_clause.data(), m_units);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool check_rup(expr* u) {
 | 
				
			||||||
 | 
					        add_units();
 | 
				
			||||||
 | 
					        mk_clause(u);
 | 
				
			||||||
 | 
					        return m_drat.is_drup(m_clause.size(), m_clause.data(), m_units);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void add_clause(expr_ref_vector const& clause) {
 | 
				
			||||||
 | 
					        mk_clause(clause);
 | 
				
			||||||
 | 
					        m_drat.add(m_clause, sat::status::input());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void check(expr_ref_vector& clause, app* proof_hint) {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (is_rup(proof_hint) && check_rup(clause)) {
 | 
				
			||||||
 | 
					            std::cout << "(verified-rup)\n";
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expr_ref_vector units(m);
 | 
				
			||||||
 | 
					        if (m_checker.check(clause, proof_hint, units)) {
 | 
				
			||||||
 | 
					            bool units_are_rup = true;
 | 
				
			||||||
 | 
					            for (expr* u : units) {
 | 
				
			||||||
 | 
					                if (!check_rup(u)) {
 | 
				
			||||||
 | 
					                    std::cout << "unit " << mk_pp(u, m) << " is not rup\n";
 | 
				
			||||||
 | 
					                    units_are_rup = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (units_are_rup) {
 | 
				
			||||||
 | 
					                std::cout << "(verified-" << proof_hint->get_name() << ")\n";
 | 
				
			||||||
 | 
					                add_clause(clause);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_solver->push();
 | 
				
			||||||
 | 
					        for (expr* lit : clause)
 | 
				
			||||||
 | 
					            m_solver->assert_expr(m.mk_not(lit));
 | 
				
			||||||
 | 
					        lbool is_sat = m_solver->check_sat();
 | 
				
			||||||
 | 
					        if (is_sat != l_false) {
 | 
				
			||||||
 | 
					            std::cout << "did not verify: " << is_sat << " " << clause << "\n\n";
 | 
				
			||||||
 | 
					            m_solver->display(std::cout);
 | 
				
			||||||
 | 
					            if (is_sat == l_true) {
 | 
				
			||||||
 | 
					                model_ref mdl;
 | 
				
			||||||
 | 
					                m_solver->get_model(mdl);
 | 
				
			||||||
 | 
					                std::cout << *mdl << "\n";
 | 
				
			||||||
 | 
					            }                
 | 
				
			||||||
 | 
					            exit(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        m_solver->pop(1);
 | 
				
			||||||
 | 
					        std::cout << "(verified-smt)\n";
 | 
				
			||||||
 | 
					        add_clause(clause);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void assume(expr_ref_vector const& clause) {
 | 
				
			||||||
 | 
					        add_clause(clause);
 | 
				
			||||||
 | 
					        m_solver->assert_expr(mk_or(clause));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class proof_cmds_imp : public proof_cmds {
 | 
				
			||||||
 | 
					    ast_manager&    m;
 | 
				
			||||||
 | 
					    expr_ref_vector m_lits;
 | 
				
			||||||
 | 
					    app_ref         m_proof_hint;
 | 
				
			||||||
 | 
					    smt_checker     m_checker;
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    proof_cmds_imp(ast_manager& m): m(m), m_lits(m), m_proof_hint(m), m_checker(m) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void add_literal(expr* e) override {
 | 
				
			||||||
 | 
					        if (m.is_proof(e))
 | 
				
			||||||
 | 
					            m_proof_hint = to_app(e);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            m_lits.push_back(e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void end_assumption() override {
 | 
				
			||||||
 | 
					        m_checker.assume(m_lits);
 | 
				
			||||||
 | 
					        m_lits.reset();
 | 
				
			||||||
 | 
					        m_proof_hint.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void end_learned() {
 | 
				
			||||||
 | 
					        m_checker.check(m_lits, m_proof_hint);
 | 
				
			||||||
 | 
					        m_lits.reset();
 | 
				
			||||||
 | 
					        m_proof_hint.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void end_deleted() {
 | 
				
			||||||
 | 
					        m_lits.reset();
 | 
				
			||||||
 | 
					        m_proof_hint.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static proof_cmds& get(cmd_context& ctx) {
 | 
				
			||||||
 | 
					    if (!ctx.get_proof_cmds())
 | 
				
			||||||
 | 
					        ctx.set_proof_cmds(alloc(proof_cmds_imp, ctx.m()));
 | 
				
			||||||
 | 
					    return *ctx.get_proof_cmds();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// assumption
 | 
				
			||||||
 | 
					class assume_cmd : public cmd {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    assume_cmd():cmd("assume") {}
 | 
				
			||||||
 | 
					    char const* get_usage() const override { return "<expr>+"; }
 | 
				
			||||||
 | 
					    char const * get_descr(cmd_context& ctx) const override { return "proof command for adding assumption (input assertion)"; }
 | 
				
			||||||
 | 
					    unsigned get_arity() const override { return VAR_ARITY; }
 | 
				
			||||||
 | 
					    void prepare(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    void finalize(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    void failure_cleanup(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; }    
 | 
				
			||||||
 | 
					    void set_next_arg(cmd_context & ctx, expr * arg) override { get(ctx).add_literal(arg); }
 | 
				
			||||||
 | 
					    void execute(cmd_context& ctx) override { get(ctx).end_assumption(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// deleted clause
 | 
				
			||||||
 | 
					class del_cmd : public cmd {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    del_cmd():cmd("del") {}
 | 
				
			||||||
 | 
					    char const* get_usage() const override { return "<expr>+"; }
 | 
				
			||||||
 | 
					    char const * get_descr(cmd_context& ctx) const override { return "proof command for clause deletion"; }
 | 
				
			||||||
 | 
					    unsigned get_arity() const override { return VAR_ARITY; }
 | 
				
			||||||
 | 
					    void prepare(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    void finalize(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    void failure_cleanup(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; }    
 | 
				
			||||||
 | 
					    void set_next_arg(cmd_context & ctx, expr * arg) override { get(ctx).add_literal(arg); }
 | 
				
			||||||
 | 
					    void execute(cmd_context& ctx) override { get(ctx).end_deleted(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// learned/redundant clause
 | 
				
			||||||
 | 
					class learn_cmd : public cmd {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    learn_cmd():cmd("learn") {}
 | 
				
			||||||
 | 
					    char const* get_usage() const override { return "<expr>+"; }
 | 
				
			||||||
 | 
					    char const* get_descr(cmd_context& ctx) const override { return "proof command for learned (redundant) clauses"; }
 | 
				
			||||||
 | 
					    unsigned get_arity() const override { return VAR_ARITY; }
 | 
				
			||||||
 | 
					    void prepare(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    void finalize(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    void failure_cleanup(cmd_context & ctx) override {}
 | 
				
			||||||
 | 
					    cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; }    
 | 
				
			||||||
 | 
					    void set_next_arg(cmd_context & ctx, expr * arg) override { get(ctx).add_literal(arg); }
 | 
				
			||||||
 | 
					    void execute(cmd_context& ctx) override { get(ctx).end_learned(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void install_proof_cmds(cmd_context & ctx) {
 | 
				
			||||||
 | 
					    ctx.insert(alloc(del_cmd));
 | 
				
			||||||
 | 
					    ctx.insert(alloc(learn_cmd));
 | 
				
			||||||
 | 
					    ctx.insert(alloc(assume_cmd));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/cmd_context/extra_cmds/proof_cmds.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/cmd_context/extra_cmds/proof_cmds.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2022 Microsoft Corporation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proof_cmds.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					    Commands for reading proofs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Nikolaj Bjorner (nbjorner) 2022-8-26
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					   proof_cmds is a structure that tracks an evidence trail.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   The main interface is to:
 | 
				
			||||||
 | 
					     add literals one by one,
 | 
				
			||||||
 | 
					     add proof hints
 | 
				
			||||||
 | 
					     until receiving end-command: assumption, learned, deleted.
 | 
				
			||||||
 | 
					   Evidence can be checked:
 | 
				
			||||||
 | 
					     - By DRUP
 | 
				
			||||||
 | 
					     - Theory lemmas
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class cmd_context;
 | 
				
			||||||
 | 
					void install_proof_cmds(cmd_context & ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -546,6 +546,7 @@ bool emonics::invariant() const {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                CTRACE("nla_solver_mons", !found, tout << "not found v" << v << ": " << m << "\n";);
 | 
					                CTRACE("nla_solver_mons", !found, tout << "not found v" << v << ": " << m << "\n";);
 | 
				
			||||||
                SASSERT(found);
 | 
					                SASSERT(found);
 | 
				
			||||||
 | 
					                (void)found;
 | 
				
			||||||
                c = c->m_next;
 | 
					                c = c->m_next;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            while (c != ht.m_head);
 | 
					            while (c != ht.m_head);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,6 +49,7 @@ const char* lp_status_to_string(lp_status status) {
 | 
				
			||||||
    case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED";
 | 
					    case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED";
 | 
				
			||||||
    case lp_status::EMPTY: return "EMPTY";
 | 
					    case lp_status::EMPTY: return "EMPTY";
 | 
				
			||||||
    case lp_status::UNSTABLE: return "UNSTABLE";
 | 
					    case lp_status::UNSTABLE: return "UNSTABLE";
 | 
				
			||||||
 | 
					    case lp_status::CANCELLED: return "CANCELLED";
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
        lp_unreachable();
 | 
					        lp_unreachable();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -380,45 +380,43 @@ namespace opt {
 | 
				
			||||||
        m_below.reset();
 | 
					        m_below.reset();
 | 
				
			||||||
        for (unsigned row_id : row_ids) {
 | 
					        for (unsigned row_id : row_ids) {
 | 
				
			||||||
            SASSERT(row_id != m_objective_id);
 | 
					            SASSERT(row_id != m_objective_id);
 | 
				
			||||||
            if (visited.contains(row_id)) {
 | 
					            if (visited.contains(row_id)) 
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            visited.insert(row_id);
 | 
					            visited.insert(row_id);
 | 
				
			||||||
            row& r = m_rows[row_id];
 | 
					            row& r = m_rows[row_id];
 | 
				
			||||||
            if (r.m_alive) {
 | 
					            if (!r.m_alive)
 | 
				
			||||||
                rational a = get_coefficient(row_id, x);
 | 
					                continue;
 | 
				
			||||||
                if (a.is_zero()) {
 | 
					            rational a = get_coefficient(row_id, x);
 | 
				
			||||||
                    // skip
 | 
					            if (a.is_zero()) {
 | 
				
			||||||
                }
 | 
					                // skip
 | 
				
			||||||
                else if (a.is_pos() == is_pos || r.m_type == t_eq) {
 | 
					 | 
				
			||||||
                    rational value = x_val - (r.m_value/a);
 | 
					 | 
				
			||||||
                    if (bound_row_index == UINT_MAX) {
 | 
					 | 
				
			||||||
                        lub_val = value;
 | 
					 | 
				
			||||||
                        bound_row_index = row_id;
 | 
					 | 
				
			||||||
                        bound_coeff = a;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else if ((value == lub_val && r.m_type == opt::t_lt) ||
 | 
					 | 
				
			||||||
                             (is_pos && value < lub_val) || 
 | 
					 | 
				
			||||||
                   
 | 
					 | 
				
			||||||
                        (!is_pos && value > lub_val)) {
 | 
					 | 
				
			||||||
                        m_above.push_back(bound_row_index);
 | 
					 | 
				
			||||||
                        lub_val = value;
 | 
					 | 
				
			||||||
                        bound_row_index = row_id;                          
 | 
					 | 
				
			||||||
                        bound_coeff = a;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    else {
 | 
					 | 
				
			||||||
                        m_above.push_back(row_id);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                else {
 | 
					 | 
				
			||||||
                    m_below.push_back(row_id);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            else if (a.is_pos() == is_pos || r.m_type == t_eq) {
 | 
				
			||||||
 | 
					                rational value = x_val - (r.m_value/a);
 | 
				
			||||||
 | 
					                if (bound_row_index == UINT_MAX) {
 | 
				
			||||||
 | 
					                    lub_val = value;
 | 
				
			||||||
 | 
					                    bound_row_index = row_id;
 | 
				
			||||||
 | 
					                    bound_coeff = a;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if ((value == lub_val && r.m_type == opt::t_lt) ||
 | 
				
			||||||
 | 
					                         (is_pos && value < lub_val) || 
 | 
				
			||||||
 | 
					                         
 | 
				
			||||||
 | 
					                         (!is_pos && value > lub_val)) {
 | 
				
			||||||
 | 
					                    m_above.push_back(bound_row_index);
 | 
				
			||||||
 | 
					                    lub_val = value;
 | 
				
			||||||
 | 
					                    bound_row_index = row_id;                          
 | 
				
			||||||
 | 
					                    bound_coeff = a;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else 
 | 
				
			||||||
 | 
					                    m_above.push_back(row_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else 
 | 
				
			||||||
 | 
					                m_below.push_back(row_id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return bound_row_index != UINT_MAX;
 | 
					        return bound_row_index != UINT_MAX;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void model_based_opt::retire_row(unsigned row_id) {
 | 
					    void model_based_opt::retire_row(unsigned row_id) {
 | 
				
			||||||
 | 
					        SASSERT(!m_retired_rows.contains(row_id));
 | 
				
			||||||
        m_rows[row_id].m_alive = false;
 | 
					        m_rows[row_id].m_alive = false;
 | 
				
			||||||
        m_retired_rows.push_back(row_id);
 | 
					        m_retired_rows.push_back(row_id);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -736,6 +734,8 @@ namespace opt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void model_based_opt::normalize(unsigned row_id) {
 | 
					    void model_based_opt::normalize(unsigned row_id) {
 | 
				
			||||||
        row& r = m_rows[row_id];
 | 
					        row& r = m_rows[row_id];
 | 
				
			||||||
 | 
					        if (!r.m_alive)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
        if (r.m_vars.empty()) {
 | 
					        if (r.m_vars.empty()) {
 | 
				
			||||||
            retire_row(row_id);
 | 
					            retire_row(row_id);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
| 
						 | 
					@ -934,6 +934,7 @@ namespace opt {
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
            row_id = m_retired_rows.back();
 | 
					            row_id = m_retired_rows.back();
 | 
				
			||||||
            m_retired_rows.pop_back();
 | 
					            m_retired_rows.pop_back();
 | 
				
			||||||
 | 
					            SASSERT(!m_rows[row_id].m_alive);
 | 
				
			||||||
            m_rows[row_id].reset();
 | 
					            m_rows[row_id].reset();
 | 
				
			||||||
            m_rows[row_id].m_alive = true;
 | 
					            m_rows[row_id].m_alive = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -995,10 +996,10 @@ namespace opt {
 | 
				
			||||||
        return v;
 | 
					        return v;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void model_based_opt::add_constraint(vector<var> const& coeffs, rational const& c, rational const& m, ineq_type rel, unsigned id) {
 | 
					    unsigned model_based_opt::add_constraint(vector<var> const& coeffs, rational const& c, rational const& m, ineq_type rel, unsigned id) {
 | 
				
			||||||
        auto const& r = m_rows.back();
 | 
					        auto const& r = m_rows.back();
 | 
				
			||||||
        if (r.m_vars == coeffs && r.m_coeff == c && r.m_mod == m && r.m_type == rel && r.m_id == id && r.m_alive)
 | 
					        if (r.m_vars == coeffs && r.m_coeff == c && r.m_mod == m && r.m_type == rel && r.m_id == id && r.m_alive)
 | 
				
			||||||
            return;
 | 
					            return m_rows.size() - 1;
 | 
				
			||||||
        unsigned row_id = new_row();
 | 
					        unsigned row_id = new_row();
 | 
				
			||||||
        set_row(row_id, coeffs, c, m, rel);
 | 
					        set_row(row_id, coeffs, c, m, rel);
 | 
				
			||||||
        m_rows[row_id].m_id = id;
 | 
					        m_rows[row_id].m_id = id;
 | 
				
			||||||
| 
						 | 
					@ -1006,6 +1007,7 @@ namespace opt {
 | 
				
			||||||
            m_var2row_ids[coeff.m_id].push_back(row_id); 
 | 
					            m_var2row_ids[coeff.m_id].push_back(row_id); 
 | 
				
			||||||
        SASSERT(invariant(row_id, m_rows[row_id]));
 | 
					        SASSERT(invariant(row_id, m_rows[row_id]));
 | 
				
			||||||
        normalize(row_id);
 | 
					        normalize(row_id);
 | 
				
			||||||
 | 
					        return row_id;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void model_based_opt::set_objective(vector<var> const& coeffs, rational const& c) {
 | 
					    void model_based_opt::set_objective(vector<var> const& coeffs, rational const& c) {
 | 
				
			||||||
| 
						 | 
					@ -1057,23 +1059,18 @@ namespace opt {
 | 
				
			||||||
        unsigned eq_row = UINT_MAX;
 | 
					        unsigned eq_row = UINT_MAX;
 | 
				
			||||||
        // select the lub and glb.
 | 
					        // select the lub and glb.
 | 
				
			||||||
        for (unsigned row_id : row_ids) {
 | 
					        for (unsigned row_id : row_ids) {
 | 
				
			||||||
            if (visited.contains(row_id)) {
 | 
					            if (visited.contains(row_id)) 
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            visited.insert(row_id);
 | 
					            visited.insert(row_id);
 | 
				
			||||||
            row& r = m_rows[row_id];
 | 
					            row& r = m_rows[row_id];
 | 
				
			||||||
            if (!r.m_alive) {
 | 
					            if (!r.m_alive) 
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            rational a = get_coefficient(row_id, x);
 | 
					            rational a = get_coefficient(row_id, x);
 | 
				
			||||||
            if (a.is_zero()) {
 | 
					            if (a.is_zero()) 
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            if (r.m_type == t_eq) 
 | 
				
			||||||
            if (r.m_type == t_eq) {
 | 
					 | 
				
			||||||
                eq_row = row_id;
 | 
					                eq_row = row_id;
 | 
				
			||||||
                continue;
 | 
					            else if (r.m_type == t_mod) 
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (r.m_type == t_mod) 
 | 
					 | 
				
			||||||
                mod_rows.push_back(row_id);
 | 
					                mod_rows.push_back(row_id);
 | 
				
			||||||
            else if (r.m_type == t_div) 
 | 
					            else if (r.m_type == t_div) 
 | 
				
			||||||
                div_rows.push_back(row_id);
 | 
					                div_rows.push_back(row_id);
 | 
				
			||||||
| 
						 | 
					@ -1106,15 +1103,12 @@ namespace opt {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!mod_rows.empty()) 
 | 
					 | 
				
			||||||
            return solve_mod(x, mod_rows, compute_def);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!div_rows.empty())
 | 
					 | 
				
			||||||
            return solve_div(x, div_rows, compute_def);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!divide_rows.empty()) 
 | 
					        if (!divide_rows.empty()) 
 | 
				
			||||||
            return solve_divides(x, divide_rows, compute_def);
 | 
					            return solve_divides(x, divide_rows, compute_def);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!div_rows.empty() || !mod_rows.empty())
 | 
				
			||||||
 | 
					            return solve_mod_div(x, mod_rows, div_rows, compute_def);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (eq_row != UINT_MAX) 
 | 
					        if (eq_row != UINT_MAX) 
 | 
				
			||||||
            return solve_for(eq_row, x, compute_def);
 | 
					            return solve_for(eq_row, x, compute_def);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1218,89 +1212,7 @@ namespace opt {
 | 
				
			||||||
    // - 0 <= g*z.value + w.value < K*(g+1)
 | 
					    // - 0 <= g*z.value + w.value < K*(g+1)
 | 
				
			||||||
    // -  add g*z + w - v - k*K = 0 for suitable k from 0 .. g based on model
 | 
					    // -  add g*z + w - v - k*K = 0 for suitable k from 0 .. g based on model
 | 
				
			||||||
    // 
 | 
					    // 
 | 
				
			||||||
    
 | 
					    //
 | 
				
			||||||
    model_based_opt::def model_based_opt::solve_mod(unsigned x, unsigned_vector const& _mod_rows, bool compute_def) {
 | 
					 | 
				
			||||||
        def result;
 | 
					 | 
				
			||||||
        unsigned_vector mod_rows(_mod_rows);
 | 
					 | 
				
			||||||
        rational K(1);
 | 
					 | 
				
			||||||
        for (unsigned ri : mod_rows)
 | 
					 | 
				
			||||||
            K = lcm(K, m_rows[ri].m_mod);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        rational x_value = m_var2value[x];
 | 
					 | 
				
			||||||
        rational y_value = div(x_value, K);
 | 
					 | 
				
			||||||
        rational z_value = mod(x_value, K);
 | 
					 | 
				
			||||||
        SASSERT(x_value == K * y_value + z_value);
 | 
					 | 
				
			||||||
        SASSERT(0 <= z_value && z_value < K);
 | 
					 | 
				
			||||||
        // add new variables
 | 
					 | 
				
			||||||
        unsigned z = add_var(z_value, true);
 | 
					 | 
				
			||||||
        unsigned y = add_var(y_value, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        uint_set visited;
 | 
					 | 
				
			||||||
        for (unsigned ri : mod_rows) {
 | 
					 | 
				
			||||||
            m_rows[ri].m_alive = false;
 | 
					 | 
				
			||||||
            visited.insert(ri);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // replace x by K*y + z in other rows.
 | 
					 | 
				
			||||||
        for (unsigned ri : m_var2row_ids[x]) {
 | 
					 | 
				
			||||||
            if (visited.contains(ri))
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            replace_var(ri, x, K, y, rational::one(), z);
 | 
					 | 
				
			||||||
            visited.insert(ri);
 | 
					 | 
				
			||||||
            normalize(ri);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // add bounds for z
 | 
					 | 
				
			||||||
        add_lower_bound(z, rational::zero());
 | 
					 | 
				
			||||||
        add_upper_bound(z, K - 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (unsigned ri : mod_rows) {
 | 
					 | 
				
			||||||
            rational a = get_coefficient(ri, x);
 | 
					 | 
				
			||||||
            replace_var(ri, x, rational::zero());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // add w = b mod K
 | 
					 | 
				
			||||||
            vector<var> coeffs = m_rows[ri].m_vars;
 | 
					 | 
				
			||||||
            rational coeff = m_rows[ri].m_coeff;
 | 
					 | 
				
			||||||
            unsigned v = m_rows[ri].m_id;
 | 
					 | 
				
			||||||
            rational v_value = m_var2value[v];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            unsigned w = UINT_MAX;
 | 
					 | 
				
			||||||
            rational offset(0);
 | 
					 | 
				
			||||||
            if (coeffs.empty())
 | 
					 | 
				
			||||||
                offset = mod(coeff, K);
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                w = add_mod(coeffs, coeff, K);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            rational w_value = w == UINT_MAX ? offset : m_var2value[w];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // add v = a*z + w - V, for k = (a*z_value + w_value) div K
 | 
					 | 
				
			||||||
            // claim: (= (mod x K) (- x (* K (div x K)))))) is a theorem for every x, K != 0
 | 
					 | 
				
			||||||
            rational V = v_value - a * z_value - w_value;
 | 
					 | 
				
			||||||
            vector<var> mod_coeffs;
 | 
					 | 
				
			||||||
            mod_coeffs.push_back(var(v, rational::minus_one()));
 | 
					 | 
				
			||||||
            mod_coeffs.push_back(var(z, a));
 | 
					 | 
				
			||||||
            if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one()));
 | 
					 | 
				
			||||||
            add_constraint(mod_coeffs, V + offset, t_eq);
 | 
					 | 
				
			||||||
            add_lower_bound(v, rational::zero());
 | 
					 | 
				
			||||||
            add_upper_bound(v, K - 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // allow to recycle row.
 | 
					 | 
				
			||||||
            m_retired_rows.push_back(ri);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            project(v, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def y_def = project(y, compute_def);
 | 
					 | 
				
			||||||
        def z_def = project(z, compute_def);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (compute_def) {
 | 
					 | 
				
			||||||
            result = (y_def * K) + z_def;
 | 
					 | 
				
			||||||
            m_var2value[x] = eval(result);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        TRACE("opt", display(tout << "solve_mod\n"));
 | 
					 | 
				
			||||||
        return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //
 | 
					    //
 | 
				
			||||||
    // Given v = a*x + b div K
 | 
					    // Given v = a*x + b div K
 | 
				
			||||||
    // Replace x |-> K*y + z
 | 
					    // Replace x |-> K*y + z
 | 
				
			||||||
| 
						 | 
					@ -1322,14 +1234,17 @@ namespace opt {
 | 
				
			||||||
    // where k is between 0 and g
 | 
					    // where k is between 0 and g
 | 
				
			||||||
    // when gcd(a, K) = 1, then there are only two cases.
 | 
					    // when gcd(a, K) = 1, then there are only two cases.
 | 
				
			||||||
    // 
 | 
					    // 
 | 
				
			||||||
    model_based_opt::def model_based_opt::solve_div(unsigned x, unsigned_vector const& _div_rows, bool compute_def) {
 | 
					    model_based_opt::def model_based_opt::solve_mod_div(unsigned x, unsigned_vector const& _mod_rows, unsigned_vector const& _div_rows, bool compute_def) {
 | 
				
			||||||
        def result;
 | 
					        def result;
 | 
				
			||||||
        unsigned_vector div_rows(_div_rows);
 | 
					        unsigned_vector div_rows(_div_rows), mod_rows(_mod_rows);
 | 
				
			||||||
        SASSERT(!div_rows.empty());
 | 
					        SASSERT(!div_rows.empty() || !mod_rows.empty());
 | 
				
			||||||
 | 
					        TRACE("opt", display(tout << "solve_div " << x << "\n"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        rational K(1);
 | 
					        rational K(1);
 | 
				
			||||||
        for (unsigned ri : div_rows)
 | 
					        for (unsigned ri : div_rows)
 | 
				
			||||||
            K = lcm(K, m_rows[ri].m_mod);
 | 
					            K = lcm(K, m_rows[ri].m_mod);
 | 
				
			||||||
 | 
					        for (unsigned ri : mod_rows)
 | 
				
			||||||
 | 
					            K = lcm(K, m_rows[ri].m_mod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        rational x_value = m_var2value[x];
 | 
					        rational x_value = m_var2value[x];
 | 
				
			||||||
        rational z_value = mod(x_value, K);
 | 
					        rational z_value = mod(x_value, K);
 | 
				
			||||||
| 
						 | 
					@ -1341,12 +1256,28 @@ namespace opt {
 | 
				
			||||||
        unsigned y = add_var(y_value, true);
 | 
					        unsigned y = add_var(y_value, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        uint_set visited;
 | 
					        uint_set visited;
 | 
				
			||||||
 | 
					        unsigned j = 0;
 | 
				
			||||||
        for (unsigned ri : div_rows) {
 | 
					        for (unsigned ri : div_rows) {
 | 
				
			||||||
 | 
					            if (visited.contains(ri))
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
            row& r = m_rows[ri];
 | 
					            row& r = m_rows[ri];
 | 
				
			||||||
            mul(ri, K / r.m_mod);
 | 
					            mul(ri, K / r.m_mod);
 | 
				
			||||||
            r.m_alive = false;
 | 
					            r.m_alive = false;
 | 
				
			||||||
            visited.insert(ri);
 | 
					            visited.insert(ri);
 | 
				
			||||||
 | 
					            div_rows[j++] = ri;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        div_rows.shrink(j);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        j = 0;
 | 
				
			||||||
 | 
					        for (unsigned ri : mod_rows) {
 | 
				
			||||||
 | 
					            if (visited.contains(ri))
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            m_rows[ri].m_alive = false;
 | 
				
			||||||
 | 
					            visited.insert(ri);
 | 
				
			||||||
 | 
					            mod_rows[j++] = ri;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        mod_rows.shrink(j);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // replace x by K*y + z in other rows.
 | 
					        // replace x by K*y + z in other rows.
 | 
				
			||||||
        for (unsigned ri : m_var2row_ids[x]) {
 | 
					        for (unsigned ri : m_var2row_ids[x]) {
 | 
				
			||||||
| 
						 | 
					@ -1361,9 +1292,10 @@ namespace opt {
 | 
				
			||||||
        add_lower_bound(z, rational::zero());
 | 
					        add_lower_bound(z, rational::zero());
 | 
				
			||||||
        add_upper_bound(z, K - 1);
 | 
					        add_upper_bound(z, K - 1);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        TRACE("opt", display(tout));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // solve for x_value = K*y_value + z_value, 0 <= z_value < K.       
 | 
					        // solve for x_value = K*y_value + z_value, 0 <= z_value < K.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned_vector vs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (unsigned ri : div_rows) {
 | 
					        for (unsigned ri : div_rows) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1375,9 +1307,11 @@ namespace opt {
 | 
				
			||||||
            rational coeff = m_rows[ri].m_coeff;
 | 
					            rational coeff = m_rows[ri].m_coeff;
 | 
				
			||||||
            unsigned w = UINT_MAX;
 | 
					            unsigned w = UINT_MAX;
 | 
				
			||||||
            rational offset(0);
 | 
					            rational offset(0);
 | 
				
			||||||
            if (coeffs.empty())
 | 
					            if (K == 1)
 | 
				
			||||||
 | 
					                offset = coeff;
 | 
				
			||||||
 | 
					            else if (coeffs.empty())
 | 
				
			||||||
                offset = div(coeff, K);
 | 
					                offset = div(coeff, K);
 | 
				
			||||||
            else
 | 
					            else 
 | 
				
			||||||
                w = add_div(coeffs, coeff, K);
 | 
					                w = add_div(coeffs, coeff, K);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //
 | 
					            //
 | 
				
			||||||
| 
						 | 
					@ -1412,20 +1346,27 @@ namespace opt {
 | 
				
			||||||
            vector<var> div_coeffs;
 | 
					            vector<var> div_coeffs;
 | 
				
			||||||
            div_coeffs.push_back(var(v, rational::minus_one()));
 | 
					            div_coeffs.push_back(var(v, rational::minus_one()));
 | 
				
			||||||
            div_coeffs.push_back(var(y, a));
 | 
					            div_coeffs.push_back(var(y, a));
 | 
				
			||||||
            if (w != UINT_MAX) div_coeffs.push_back(var(w, rational::one()));
 | 
					            if (w != UINT_MAX) 
 | 
				
			||||||
 | 
					                div_coeffs.push_back(var(w, rational::one()));
 | 
				
			||||||
 | 
					            else if (K == 1) 
 | 
				
			||||||
 | 
					                div_coeffs.append(coeffs);
 | 
				
			||||||
            add_constraint(div_coeffs, k + offset, t_eq);
 | 
					            add_constraint(div_coeffs, k + offset, t_eq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            unsigned u = UINT_MAX;
 | 
					            unsigned u = UINT_MAX;
 | 
				
			||||||
            offset = 0;
 | 
					            offset = 0;
 | 
				
			||||||
            if (coeffs.empty())
 | 
					            if (K == 1)
 | 
				
			||||||
 | 
					                offset = 0;
 | 
				
			||||||
 | 
					            else if (coeffs.empty())
 | 
				
			||||||
                offset = mod(coeff, K);
 | 
					                offset = mod(coeff, K);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
                u = add_mod(coeffs, coeff, K);
 | 
					                u = add_mod(coeffs, coeff, K);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // add a*z + (b mod K) < (k + 1)*K
 | 
					            // add a*z + (b mod K) < (k + 1)*K
 | 
				
			||||||
            vector<var> bound_coeffs;
 | 
					            vector<var> bound_coeffs;
 | 
				
			||||||
            bound_coeffs.push_back(var(z, a));
 | 
					            bound_coeffs.push_back(var(z, a));
 | 
				
			||||||
            if (u != UINT_MAX) bound_coeffs.push_back(var(u, rational::one()));
 | 
					            if (u != UINT_MAX)
 | 
				
			||||||
 | 
					                bound_coeffs.push_back(var(u, rational::one()));
 | 
				
			||||||
            add_constraint(bound_coeffs, 1 - K * (k + 1) + offset, t_le);
 | 
					            add_constraint(bound_coeffs, 1 - K * (k + 1) + offset, t_le);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // add k*K <= az + (b mod K)
 | 
					            // add k*K <= az + (b mod K)
 | 
				
			||||||
| 
						 | 
					@ -1433,11 +1374,49 @@ namespace opt {
 | 
				
			||||||
                c.m_coeff.neg();
 | 
					                c.m_coeff.neg();
 | 
				
			||||||
            add_constraint(bound_coeffs, k * K - offset, t_le);
 | 
					            add_constraint(bound_coeffs, k * K - offset, t_le);
 | 
				
			||||||
            // allow to recycle row.
 | 
					            // allow to recycle row.
 | 
				
			||||||
            m_retired_rows.push_back(ri);
 | 
					            retire_row(ri);
 | 
				
			||||||
            project(v, false);
 | 
					            vs.push_back(v);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TRACE("opt", display(tout << "solve_div reduced " << y << " " << z << "\n"));
 | 
					        for (unsigned ri : mod_rows) {
 | 
				
			||||||
 | 
					            rational a = get_coefficient(ri, x);
 | 
				
			||||||
 | 
					            replace_var(ri, x, rational::zero());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // add w = b mod K
 | 
				
			||||||
 | 
					            vector<var> coeffs = m_rows[ri].m_vars;
 | 
				
			||||||
 | 
					            rational coeff = m_rows[ri].m_coeff;
 | 
				
			||||||
 | 
					            unsigned v = m_rows[ri].m_id;
 | 
				
			||||||
 | 
					            rational v_value = m_var2value[v];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            unsigned w = UINT_MAX;
 | 
				
			||||||
 | 
					            rational offset(0);
 | 
				
			||||||
 | 
					            if (coeffs.empty() || K == 1)
 | 
				
			||||||
 | 
					                offset = mod(coeff, K);
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                w = add_mod(coeffs, coeff, K);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            rational w_value = w == UINT_MAX ? offset : m_var2value[w];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // add v = a*z + w - V, for k = (a*z_value + w_value) div K
 | 
				
			||||||
 | 
					            // claim: (= (mod x K) (- x (* K (div x K)))))) is a theorem for every x, K != 0
 | 
				
			||||||
 | 
					            rational V = v_value - a * z_value - w_value;
 | 
				
			||||||
 | 
					            vector<var> mod_coeffs;
 | 
				
			||||||
 | 
					            mod_coeffs.push_back(var(v, rational::minus_one()));
 | 
				
			||||||
 | 
					            mod_coeffs.push_back(var(z, a));
 | 
				
			||||||
 | 
					            if (w != UINT_MAX) mod_coeffs.push_back(var(w, rational::one()));
 | 
				
			||||||
 | 
					            add_constraint(mod_coeffs, V + offset, t_eq);
 | 
				
			||||||
 | 
					            add_lower_bound(v, rational::zero());
 | 
				
			||||||
 | 
					            add_upper_bound(v, K - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            retire_row(ri);
 | 
				
			||||||
 | 
					            vs.push_back(v);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned v : vs)
 | 
				
			||||||
 | 
					            project(v, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // project internal variables.
 | 
					        // project internal variables.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def y_def = project(y, compute_def);
 | 
					        def y_def = project(y, compute_def);
 | 
				
			||||||
| 
						 | 
					@ -1501,12 +1480,12 @@ namespace opt {
 | 
				
			||||||
        unsigned_vector const& row_ids = m_var2row_ids[x];
 | 
					        unsigned_vector const& row_ids = m_var2row_ids[x];
 | 
				
			||||||
        uint_set visited;
 | 
					        uint_set visited;
 | 
				
			||||||
        for (unsigned row_id : row_ids) {           
 | 
					        for (unsigned row_id : row_ids) {           
 | 
				
			||||||
            if (!visited.contains(row_id)) {
 | 
					            if (visited.contains(row_id))
 | 
				
			||||||
                // x |-> D*y + u
 | 
					                continue;
 | 
				
			||||||
                replace_var(row_id, x, D, y, u);
 | 
					            // x |-> D*y + u
 | 
				
			||||||
                visited.insert(row_id);
 | 
					            replace_var(row_id, x, D, y, u);
 | 
				
			||||||
                normalize(row_id);
 | 
					            visited.insert(row_id);
 | 
				
			||||||
            }
 | 
					            normalize(row_id);            
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        TRACE("opt1", display(tout << "tableau after replace x by y := v" << y << "\n"););
 | 
					        TRACE("opt1", display(tout << "tableau after replace x by y := v" << y << "\n"););
 | 
				
			||||||
        def result = project(y, compute_def);
 | 
					        def result = project(y, compute_def);
 | 
				
			||||||
| 
						 | 
					@ -1611,25 +1590,28 @@ namespace opt {
 | 
				
			||||||
        uint_set visited;
 | 
					        uint_set visited;
 | 
				
			||||||
        visited.insert(row_id1);
 | 
					        visited.insert(row_id1);
 | 
				
			||||||
        for (unsigned row_id2 : row_ids) {
 | 
					        for (unsigned row_id2 : row_ids) {
 | 
				
			||||||
            if (!visited.contains(row_id2)) {                
 | 
					            if (visited.contains(row_id2))
 | 
				
			||||||
                visited.insert(row_id2);                
 | 
					                continue;
 | 
				
			||||||
                b = get_coefficient(row_id2, x);
 | 
					            visited.insert(row_id2);
 | 
				
			||||||
                if (b.is_zero())
 | 
					            row& r = m_rows[row_id2];
 | 
				
			||||||
                    continue;
 | 
					            if (!r.m_alive)
 | 
				
			||||||
                row& dst = m_rows[row_id2];
 | 
					                continue;
 | 
				
			||||||
                switch (dst.m_type) {
 | 
					            b = get_coefficient(row_id2, x);
 | 
				
			||||||
                case t_eq:
 | 
					            if (b.is_zero())
 | 
				
			||||||
                case t_lt:
 | 
					                continue;
 | 
				
			||||||
                case t_le:
 | 
					            row& dst = m_rows[row_id2];
 | 
				
			||||||
                    solve(row_id1, a, row_id2, x);
 | 
					            switch (dst.m_type) {
 | 
				
			||||||
                    break;
 | 
					            case t_eq:
 | 
				
			||||||
                case t_divides:
 | 
					            case t_lt:
 | 
				
			||||||
                case t_mod:
 | 
					            case t_le:
 | 
				
			||||||
                case t_div:
 | 
					                solve(row_id1, a, row_id2, x);
 | 
				
			||||||
                    // mod reduction already done.
 | 
					                break;
 | 
				
			||||||
                    UNREACHABLE();
 | 
					            case t_divides:
 | 
				
			||||||
                    break;
 | 
					            case t_mod:
 | 
				
			||||||
                }
 | 
					            case t_div:
 | 
				
			||||||
 | 
					                // mod reduction already done.
 | 
				
			||||||
 | 
					                UNREACHABLE();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        def result;
 | 
					        def result;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,14 +60,13 @@ namespace opt {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        struct row {
 | 
					        struct row {
 | 
				
			||||||
            row(): m_type(t_le), m_value(0), m_alive(false) {}
 | 
					            vector<var> m_vars;               // variables with coefficients
 | 
				
			||||||
            vector<var> m_vars;         // variables with coefficients
 | 
					            rational    m_coeff = rational::zero();          // constant in inequality
 | 
				
			||||||
            rational    m_coeff;        // constant in inequality
 | 
					            rational    m_mod = rational::zero();            // value the term divide
 | 
				
			||||||
            rational    m_mod;          // value the term divide
 | 
					            ineq_type   m_type = t_le;        // inequality type
 | 
				
			||||||
            ineq_type   m_type;         // inequality type
 | 
					            rational    m_value = rational::zero();          // value of m_vars + m_coeff under interpretation of m_var2value.
 | 
				
			||||||
            rational    m_value;        // value of m_vars + m_coeff under interpretation of m_var2value.
 | 
					            bool        m_alive = false;      // rows can be marked dead if they have been processed.
 | 
				
			||||||
            bool        m_alive;        // rows can be marked dead if they have been processed.
 | 
					            unsigned    m_id = UINT_MAX;      // variable defined by row (used for mod_t and div_t)
 | 
				
			||||||
            unsigned    m_id;           // variable defined by row (used for mod_t and div_t)
 | 
					 | 
				
			||||||
            void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); }
 | 
					            void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            row& normalize();
 | 
					            row& normalize();
 | 
				
			||||||
| 
						 | 
					@ -139,7 +138,7 @@ namespace opt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void add_upper_bound(unsigned x, rational const& hi);
 | 
					        void add_upper_bound(unsigned x, rational const& hi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void add_constraint(vector<var> const& coeffs, rational const& c, rational const& m, ineq_type r, unsigned id);
 | 
					        unsigned add_constraint(vector<var> const& coeffs, rational const& c, rational const& m, ineq_type r, unsigned id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B);
 | 
					        void replace_var(unsigned row_id, unsigned x, rational const& A, unsigned y, rational const& B);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,9 +166,7 @@ namespace opt {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def solve_divides(unsigned x, unsigned_vector const& divide_rows, bool compute_def);
 | 
					        def solve_divides(unsigned x, unsigned_vector const& divide_rows, bool compute_def);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def solve_mod(unsigned x, unsigned_vector const& divide_rows, bool compute_def);
 | 
					        def solve_mod_div(unsigned x, unsigned_vector const& mod_rows, unsigned_vector const& divide_rows, bool compute_def);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        def solve_div(unsigned x, unsigned_vector const& divide_rows, bool compute_def);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool is_int(unsigned x) const { return m_var2is_int[x]; }
 | 
					        bool is_int(unsigned x) const { return m_var2is_int[x]; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,10 @@ namespace simplex {
 | 
				
			||||||
        sparse_matrix_ops::kernel(M, K);
 | 
					        sparse_matrix_ops::kernel(M, K);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void kernel_ffe(sparse_matrix<mpq_ext> &M, vector<vector<rational>> &K) {
 | 
				
			||||||
 | 
					      sparse_matrix_ops::kernel_ffe(M, K);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void ensure_rational_solution(simplex<mpq_ext>& S) {
 | 
					    void ensure_rational_solution(simplex<mpq_ext>& S) {
 | 
				
			||||||
        rational delta(1);
 | 
					        rational delta(1);
 | 
				
			||||||
        for (unsigned i = 0; i < S.get_num_vars(); ++i) {
 | 
					        for (unsigned i = 0; i < S.get_num_vars(); ++i) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -203,5 +203,6 @@ namespace simplex {
 | 
				
			||||||
    void ensure_rational_solution(simplex<mpq_ext>& s);
 | 
					    void ensure_rational_solution(simplex<mpq_ext>& s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void kernel(sparse_matrix<mpq_ext>& s, vector<vector<rational>>& K);
 | 
					    void kernel(sparse_matrix<mpq_ext>& s, vector<vector<rational>>& K);
 | 
				
			||||||
 | 
					    void kernel_ffe(sparse_matrix<mpq_ext> &s, vector<vector<rational>> &K);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -168,6 +168,7 @@ namespace simplex {
 | 
				
			||||||
        void add_var(row r, numeral const& n, var_t var);
 | 
					        void add_var(row r, numeral const& n, var_t var);
 | 
				
			||||||
        void add(row r, numeral const& n, row src);
 | 
					        void add(row r, numeral const& n, row src);
 | 
				
			||||||
        void mul(row r, numeral const& n);
 | 
					        void mul(row r, numeral const& n);
 | 
				
			||||||
 | 
					        void div(row r, numeral const& n);
 | 
				
			||||||
        void neg(row r);
 | 
					        void neg(row r);
 | 
				
			||||||
        void del(row r);
 | 
					        void del(row r);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -432,6 +432,25 @@ namespace simplex {
 | 
				
			||||||
        }                       
 | 
					        }                       
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					       \brief Set row <- n/row
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    template <typename Ext>
 | 
				
			||||||
 | 
					    void sparse_matrix<Ext>::div(row r, numeral const &n) {
 | 
				
			||||||
 | 
					      SASSERT(!m.is_zero(n));
 | 
				
			||||||
 | 
					      if (m.is_one(n)) {
 | 
				
			||||||
 | 
					        // no op
 | 
				
			||||||
 | 
					      } else if (m.is_minus_one(n)) {
 | 
				
			||||||
 | 
					        neg(r);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        row_iterator it = row_begin(r);
 | 
				
			||||||
 | 
					        row_iterator end = row_end(r);
 | 
				
			||||||
 | 
					        for (; it != end; ++it) {
 | 
				
			||||||
 | 
					          m.div(it->m_coeff, n, it->m_coeff);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
       \brief Delete row.
 | 
					       \brief Delete row.
 | 
				
			||||||
    */    
 | 
					    */    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,7 +43,7 @@ class sparse_matrix_ops {
 | 
				
			||||||
            for (auto [row, row_entry] : M.get_rows(k)) {
 | 
					            for (auto [row, row_entry] : M.get_rows(k)) {
 | 
				
			||||||
                if (c[row.id()] != 0) continue;
 | 
					                if (c[row.id()] != 0) continue;
 | 
				
			||||||
                auto &m_jk = row_entry->m_coeff;
 | 
					                auto &m_jk = row_entry->m_coeff;
 | 
				
			||||||
                if (mpq_manager<false>::is_zero(m_jk)) continue;
 | 
					                if (m.is_zero(m_jk)) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // D = rational(-1) / m_jk;
 | 
					                // D = rational(-1) / m_jk;
 | 
				
			||||||
                m.set(D, m_jk);
 | 
					                m.set(D, m_jk);
 | 
				
			||||||
| 
						 | 
					@ -68,9 +68,10 @@ class sparse_matrix_ops {
 | 
				
			||||||
            K.push_back(vector<rational>());
 | 
					            K.push_back(vector<rational>());
 | 
				
			||||||
            for (unsigned i = 0; i < n_vars; ++i) {
 | 
					            for (unsigned i = 0; i < n_vars; ++i) {
 | 
				
			||||||
                if (d[i] > 0) {
 | 
					                if (d[i] > 0) {
 | 
				
			||||||
                    auto r = sparse_matrix<mpq_ext>::row(d[i] - 1);
 | 
					                    auto r = typename sparse_matrix<Ext>::row(d[i] - 1);
 | 
				
			||||||
                    K.back().push_back(rational(M.get_coeff(r, k)));
 | 
					                    K.back().push_back(rational(M.get_coeff(r, k)));
 | 
				
			||||||
                } else if (i == k)
 | 
					                }
 | 
				
			||||||
 | 
					                else if (i == k)
 | 
				
			||||||
                    K.back().push_back(rational(1));
 | 
					                    K.back().push_back(rational(1));
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    K.back().push_back(rational(0));
 | 
					                    K.back().push_back(rational(0));
 | 
				
			||||||
| 
						 | 
					@ -81,5 +82,157 @@ class sparse_matrix_ops {
 | 
				
			||||||
    static void kernel(sparse_matrix<mpq_ext> &M, vector<vector<rational>> &K) {
 | 
					    static void kernel(sparse_matrix<mpq_ext> &M, vector<vector<rational>> &K) {
 | 
				
			||||||
        kernel<mpq_ext>(M, K);
 | 
					        kernel<mpq_ext>(M, K);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// \brief Kernel computation using fraction-free-elimination
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    template <typename Ext>
 | 
				
			||||||
 | 
					    static void kernel_ffe(sparse_matrix<Ext> &M, vector<vector<rational>> &K) {
 | 
				
			||||||
 | 
					        using scoped_numeral = typename Ext::scoped_numeral;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Based on 	George Nakos, Peter R. Turner, Robert M. Williams:
 | 
				
			||||||
 | 
					        /// Fraction-free algorithms for linear and polynomial equations. SIGSAM
 | 
				
			||||||
 | 
					        /// Bull. 31(3): 11-19 (1997)
 | 
				
			||||||
 | 
					        vector<unsigned> d, c;
 | 
				
			||||||
 | 
					        unsigned n_vars = M.num_vars(), n_rows = M.num_rows();
 | 
				
			||||||
 | 
					        c.resize(n_rows, 0u);
 | 
				
			||||||
 | 
					        d.resize(n_vars, 0u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto &m = M.get_manager();
 | 
				
			||||||
 | 
					        scoped_numeral m_ik(m);
 | 
				
			||||||
 | 
					        scoped_numeral m_jk(m);
 | 
				
			||||||
 | 
					        scoped_numeral last_pv(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m.set(last_pv, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned k = 0; k < n_vars; ++k) {
 | 
				
			||||||
 | 
					            d[k] = 0;
 | 
				
			||||||
 | 
					            for (auto [row, row_entry] : M.get_rows(k)) {
 | 
				
			||||||
 | 
					                if (c[row.id()] != 0) continue;
 | 
				
			||||||
 | 
					                auto &m_jk_ref = row_entry->m_coeff;
 | 
				
			||||||
 | 
					                if (m.is_zero(m_jk_ref))
 | 
				
			||||||
 | 
					                    // XXX: should not happen, the matrix is sparse
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // this a pivot column
 | 
				
			||||||
 | 
					                m.set(m_jk, m_jk_ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // ensure that pivot is negative
 | 
				
			||||||
 | 
					                if (m.is_pos(m_jk_ref)) { M.neg(row); }
 | 
				
			||||||
 | 
					                else { m.neg(m_jk); }
 | 
				
			||||||
 | 
					                // m_jk is abs(M[j]][k])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (auto row_i : M.get_rows()) {
 | 
				
			||||||
 | 
					                    if (row_i.id() == row.id()) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    m.set(m_ik, M.get_coeff(row_i, k));
 | 
				
			||||||
 | 
					                    // row_i *= m_jk
 | 
				
			||||||
 | 
					                    M.mul(row_i, m_jk);
 | 
				
			||||||
 | 
					                    if (!m.is_zero(m_ik)) {
 | 
				
			||||||
 | 
					                        // row_i += m_ik * row
 | 
				
			||||||
 | 
					                        M.add(row_i, m_ik, row);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    M.div(row_i, last_pv);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                c[row.id()] = k + 1;
 | 
				
			||||||
 | 
					                d[k] = row.id() + 1;
 | 
				
			||||||
 | 
					                m.set(last_pv, m_jk);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned k = 0; k < n_vars; ++k) {
 | 
				
			||||||
 | 
					            if (d[k] != 0) continue;
 | 
				
			||||||
 | 
					            K.push_back(vector<rational>());
 | 
				
			||||||
 | 
					            for (unsigned i = 0; i < n_vars; ++i) {
 | 
				
			||||||
 | 
					                if (d[i] > 0) {
 | 
				
			||||||
 | 
					                    auto r = typename sparse_matrix<Ext>::row(d[i] - 1);
 | 
				
			||||||
 | 
					                    K.back().push_back(rational(M.get_coeff(r, k)));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (i == k)
 | 
				
			||||||
 | 
					                    K.back().push_back(rational(last_pv));
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    K.back().push_back(rational(0));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static void kernel_ffe(sparse_matrix<mpq_ext> &M,
 | 
				
			||||||
 | 
					                           vector<vector<rational>> &K) {
 | 
				
			||||||
 | 
					        kernel_ffe<mpq_ext>(M, K);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template <typename Ext>
 | 
				
			||||||
 | 
					    static void kernel_ffe(sparse_matrix<Ext> &M, sparse_matrix<Ext> &K,
 | 
				
			||||||
 | 
					                           vector<unsigned> &basics) {
 | 
				
			||||||
 | 
					        using scoped_numeral = typename Ext::scoped_numeral;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Based on 	George Nakos, Peter R. Turner, Robert M. Williams:
 | 
				
			||||||
 | 
					        /// Fraction-free algorithms for linear and polynomial equations. SIGSAM
 | 
				
			||||||
 | 
					        /// Bull. 31(3): 11-19 (1997)
 | 
				
			||||||
 | 
					        vector<unsigned> d, c;
 | 
				
			||||||
 | 
					        unsigned n_vars = M.num_vars(), n_rows = M.num_rows();
 | 
				
			||||||
 | 
					        c.resize(n_rows, 0u);
 | 
				
			||||||
 | 
					        d.resize(n_vars, 0u);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto &m = M.get_manager();
 | 
				
			||||||
 | 
					        scoped_numeral m_ik(m);
 | 
				
			||||||
 | 
					        scoped_numeral m_jk(m);
 | 
				
			||||||
 | 
					        scoped_numeral last_pv(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m.set(last_pv, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned k = 0; k < n_vars; ++k) {
 | 
				
			||||||
 | 
					            d[k] = 0;
 | 
				
			||||||
 | 
					            for (auto [row, row_entry] : M.get_rows(k)) {
 | 
				
			||||||
 | 
					                if (c[row.id()] != 0) continue;
 | 
				
			||||||
 | 
					                auto &m_jk_ref = row_entry->m_coeff;
 | 
				
			||||||
 | 
					                if (m.is_zero(m_jk_ref))
 | 
				
			||||||
 | 
					                    // XXX: should not happen, the matrix is sparse
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // this a pivot column
 | 
				
			||||||
 | 
					                m.set(m_jk, m_jk_ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // ensure that pivot is negative
 | 
				
			||||||
 | 
					                if (m.is_pos(m_jk_ref)) { M.neg(row); }
 | 
				
			||||||
 | 
					                else { m.neg(m_jk); }
 | 
				
			||||||
 | 
					                // m_jk is abs(M[j]][k])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (auto row_i : M.get_rows()) {
 | 
				
			||||||
 | 
					                    if (row_i.id() == row.id()) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    m.set(m_ik, M.get_coeff(row_i, k));
 | 
				
			||||||
 | 
					                    // row_i *= m_jk
 | 
				
			||||||
 | 
					                    M.mul(row_i, m_jk);
 | 
				
			||||||
 | 
					                    if (!m.is_zero(m_ik)) {
 | 
				
			||||||
 | 
					                        // row_i += m_ik * row
 | 
				
			||||||
 | 
					                        M.add(row_i, m_ik, row);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    M.div(row_i, last_pv);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                c[row.id()] = k + 1;
 | 
				
			||||||
 | 
					                d[k] = row.id() + 1;
 | 
				
			||||||
 | 
					                m.set(last_pv, m_jk);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        K.ensure_var(n_vars - 1);
 | 
				
			||||||
 | 
					        for (unsigned k = 0; k < n_vars; ++k) {
 | 
				
			||||||
 | 
					            if (d[k] != 0) continue;
 | 
				
			||||||
 | 
					            auto row = K.mk_row();
 | 
				
			||||||
 | 
					            basics.push_back(k);
 | 
				
			||||||
 | 
					            for (unsigned i = 0; i < n_vars; ++i) {
 | 
				
			||||||
 | 
					                if (d[i] > 0) {
 | 
				
			||||||
 | 
					                    auto r = typename sparse_matrix<Ext>::row(d[i] - 1);
 | 
				
			||||||
 | 
					                    K.add_var(row, M.get_coeff(r, k), i);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (i == k)
 | 
				
			||||||
 | 
					                  K.add_var(row, last_pv, i);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
} // namespace simplex
 | 
					} // namespace simplex
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -566,14 +566,13 @@ struct evaluator_cfg : public default_rewriter_cfg {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case, bool& are_unique) {
 | 
					    bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case, bool& are_unique) {
 | 
				
			||||||
        SASSERT(m_ar.is_array(a));
 | 
					        SASSERT(m_ar.is_array(a));
 | 
				
			||||||
        bool are_values = true;
 | 
					 | 
				
			||||||
        are_unique = true;
 | 
					        are_unique = true;
 | 
				
			||||||
        TRACE("model_evaluator", tout << mk_pp(a, m) << "\n";);
 | 
					        TRACE("model_evaluator", tout << mk_pp(a, m) << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while (m_ar.is_store(a)) {
 | 
					        while (m_ar.is_store(a)) {
 | 
				
			||||||
            expr_ref_vector store(m);
 | 
					            expr_ref_vector store(m);
 | 
				
			||||||
            store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1);
 | 
					            store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1);
 | 
				
			||||||
            are_values &= args_are_values(store, are_unique);
 | 
					            args_are_values(store, are_unique);
 | 
				
			||||||
            stores.push_back(store);
 | 
					            stores.push_back(store);
 | 
				
			||||||
            a = to_app(a)->get_arg(0);
 | 
					            a = to_app(a)->get_arg(0);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -584,9 +583,8 @@ struct evaluator_cfg : public default_rewriter_cfg {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (m_ar_rw.has_index_set(a, else_case, stores)) {
 | 
					        if (m_ar_rw.has_index_set(a, else_case, stores)) {
 | 
				
			||||||
            for (auto const& store : stores) {
 | 
					            for (auto const& store : stores) 
 | 
				
			||||||
                are_values &= args_are_values(store, are_unique);
 | 
					                args_are_values(store, are_unique);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!m_ar.is_as_array(a)) {
 | 
					        if (!m_ar.is_as_array(a)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,7 +169,6 @@ def_module_params('fp',
 | 
				
			||||||
                          ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'),
 | 
					                          ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'),
 | 
				
			||||||
                          ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"),
 | 
					                          ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"),
 | 
				
			||||||
                          ('spacer.min_level', UINT, 0, 'Minimal level to explore'),
 | 
					                          ('spacer.min_level', UINT, 0, 'Minimal level to explore'),
 | 
				
			||||||
                          ('spacer.print_json', SYMBOL, '', 'Print pobs tree in JSON format to a given file'),
 | 
					 | 
				
			||||||
                          ('spacer.trace_file', SYMBOL, '', 'Log file for progress events'),
 | 
					                          ('spacer.trace_file', SYMBOL, '', 'Log file for progress events'),
 | 
				
			||||||
                          ('spacer.ctp', BOOL, True, 'Enable counterexample-to-pushing'),
 | 
					                          ('spacer.ctp', BOOL, True, 'Enable counterexample-to-pushing'),
 | 
				
			||||||
                          ('spacer.use_inc_clause', BOOL, True, 'Use incremental clause to represent trans'),
 | 
					                          ('spacer.use_inc_clause', BOOL, True, 'Use incremental clause to represent trans'),
 | 
				
			||||||
| 
						 | 
					@ -181,4 +180,10 @@ def_module_params('fp',
 | 
				
			||||||
                          ('spacer.use_lim_num_gen', BOOL, False, 'Enable limit numbers generalizer to get smaller numbers'),
 | 
					                          ('spacer.use_lim_num_gen', BOOL, False, 'Enable limit numbers generalizer to get smaller numbers'),
 | 
				
			||||||
                          ('spacer.logic', SYMBOL, '', 'SMT-LIB logic to configure internal SMT solvers'),
 | 
					                          ('spacer.logic', SYMBOL, '', 'SMT-LIB logic to configure internal SMT solvers'),
 | 
				
			||||||
                          ('spacer.arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'),
 | 
					                          ('spacer.arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'),
 | 
				
			||||||
 | 
					                          ('spacer.global', BOOL, False, 'Enable global guidance'),
 | 
				
			||||||
 | 
					                          ('spacer.gg.concretize', BOOL, True, 'Enable global guidance concretize'),
 | 
				
			||||||
 | 
					                          ('spacer.gg.conjecture', BOOL, True, 'Enable global guidance conjecture'),
 | 
				
			||||||
 | 
					                          ('spacer.gg.subsume', BOOL, True, 'Enable global guidance subsume'),
 | 
				
			||||||
 | 
					                          ('spacer.use_iuc', BOOL, True, 'Enable Interpolating Unsat Core(IUC) for lemma generalization'),
 | 
				
			||||||
 | 
					                          ('spacer.expand_bnd', BOOL, False, 'Enable expand-bound lemma generalization'),
 | 
				
			||||||
                          ))
 | 
					                          ))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										8
									
								
								src/muz/spacer/.clang-format
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/muz/spacer/.clang-format
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					BasedOnStyle: LLVM
 | 
				
			||||||
 | 
					AllowShortFunctionsOnASingleLine: All
 | 
				
			||||||
 | 
					IndentWidth: '4'
 | 
				
			||||||
 | 
					AllowShortBlocksOnASingleLine: true
 | 
				
			||||||
 | 
					AllowShortIfStatementsOnASingleLine: true
 | 
				
			||||||
 | 
					AllowShortLoopsOnASingleLine: true
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ z3_add_component(spacer
 | 
				
			||||||
  spacer_prop_solver.cpp
 | 
					  spacer_prop_solver.cpp
 | 
				
			||||||
  spacer_sym_mux.cpp
 | 
					  spacer_sym_mux.cpp
 | 
				
			||||||
  spacer_util.cpp
 | 
					  spacer_util.cpp
 | 
				
			||||||
 | 
					  spacer_cluster_util.cpp
 | 
				
			||||||
  spacer_iuc_solver.cpp
 | 
					  spacer_iuc_solver.cpp
 | 
				
			||||||
  spacer_legacy_mbp.cpp
 | 
					  spacer_legacy_mbp.cpp
 | 
				
			||||||
  spacer_proof_utils.cpp
 | 
					  spacer_proof_utils.cpp
 | 
				
			||||||
| 
						 | 
					@ -22,12 +23,19 @@ z3_add_component(spacer
 | 
				
			||||||
  spacer_sem_matcher.cpp
 | 
					  spacer_sem_matcher.cpp
 | 
				
			||||||
  spacer_quant_generalizer.cpp
 | 
					  spacer_quant_generalizer.cpp
 | 
				
			||||||
  spacer_arith_generalizers.cpp
 | 
					  spacer_arith_generalizers.cpp
 | 
				
			||||||
 | 
					  spacer_global_generalizer.cpp
 | 
				
			||||||
 | 
					  spacer_ind_lemma_generalizer.cpp
 | 
				
			||||||
 | 
					  spacer_expand_bnd_generalizer.cpp
 | 
				
			||||||
 | 
					  spacer_cluster.cpp
 | 
				
			||||||
  spacer_callback.cpp
 | 
					  spacer_callback.cpp
 | 
				
			||||||
  spacer_json.cpp
 | 
					 | 
				
			||||||
  spacer_iuc_proof.cpp
 | 
					  spacer_iuc_proof.cpp
 | 
				
			||||||
  spacer_mbc.cpp
 | 
					  spacer_mbc.cpp
 | 
				
			||||||
  spacer_pdr.cpp
 | 
					  spacer_pdr.cpp
 | 
				
			||||||
  spacer_sat_answer.cpp
 | 
					  spacer_sat_answer.cpp
 | 
				
			||||||
 | 
					  spacer_concretize.cpp
 | 
				
			||||||
 | 
					  spacer_convex_closure.cpp
 | 
				
			||||||
 | 
					  spacer_conjecture.cpp
 | 
				
			||||||
 | 
					  spacer_arith_kernel.cpp
 | 
				
			||||||
  COMPONENT_DEPENDENCIES
 | 
					  COMPONENT_DEPENDENCIES
 | 
				
			||||||
  arith_tactics
 | 
					  arith_tactics
 | 
				
			||||||
  core_tactics
 | 
					  core_tactics
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,6 +155,7 @@ void anti_unifier::operator()(expr *e1, expr *e2, expr_ref &res,
 | 
				
			||||||
            m_pinned.push_back(u);
 | 
					            m_pinned.push_back(u);
 | 
				
			||||||
            m_cache.insert(n1, n2, u);
 | 
					            m_cache.insert(n1, n2, u);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        m_todo.pop_back();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expr *r;
 | 
					    expr *r;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										108
									
								
								src/muz/spacer/spacer_arith_kernel.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/muz/spacer/spacer_arith_kernel.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,108 @@
 | 
				
			||||||
 | 
					/**++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spacer_arith_kernel.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Compute kernel of a matrix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Hari Govind
 | 
				
			||||||
 | 
					    Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_arith_kernel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "math/simplex/sparse_matrix_def.h"
 | 
				
			||||||
 | 
					#include "math/simplex/sparse_matrix_ops.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace spacer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool spacer_arith_kernel::compute_kernel() {
 | 
				
			||||||
 | 
					    SASSERT(m_matrix.num_rows() > 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (false && m_matrix.compute_linear_deps(m_kernel)) {
 | 
				
			||||||
 | 
					        // the matrix cannot be reduced further
 | 
				
			||||||
 | 
					        if (m_matrix.num_cols() - m_kernel.num_rows() <= 1) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_kernel.reset(m_kernel.num_cols());
 | 
				
			||||||
 | 
					        SASSERT(m_matrix.num_cols() > 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m_matrix.num_cols() > 2) m_st.m_failed++;
 | 
				
			||||||
 | 
					    if (m_plugin /* && m_matrix.num_cols() > 2 */) {
 | 
				
			||||||
 | 
					        return m_plugin->compute_kernel(m_matrix, m_kernel, m_basic_vars);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					class simplex_arith_kernel_plugin : public spacer_arith_kernel::plugin {
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    simplex_arith_kernel_plugin() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool compute_kernel(const spacer_matrix &in, spacer_matrix &out,
 | 
				
			||||||
 | 
					                        vector<unsigned> &basics) override {
 | 
				
			||||||
 | 
					        using qmatrix = simplex::sparse_matrix<simplex::mpq_ext>;
 | 
				
			||||||
 | 
					        unsynch_mpq_manager m;
 | 
				
			||||||
 | 
					        qmatrix qmat(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // extra column for column of 1
 | 
				
			||||||
 | 
					        qmat.ensure_var(in.num_cols());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned i = 0, n_rows = in.num_rows(); i < n_rows; ++i) {
 | 
				
			||||||
 | 
					            auto row_id = qmat.mk_row();
 | 
				
			||||||
 | 
					            unsigned j, n_cols;
 | 
				
			||||||
 | 
					            for (j = 0, n_cols = in.num_cols(); j < n_cols; ++j) {
 | 
				
			||||||
 | 
					                qmat.add_var(row_id, in.get(i, j).to_mpq(), j);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            qmat.add_var(row_id, rational::one().to_mpq(), n_cols);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        TRACE("gg", qmat.display(tout););
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        qmatrix kern(m);
 | 
				
			||||||
 | 
					        simplex::sparse_matrix_ops::kernel_ffe<simplex::mpq_ext>(qmat, kern,
 | 
				
			||||||
 | 
					                                                                 basics);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        out.reset(kern.num_vars());
 | 
				
			||||||
 | 
					        vector<rational> vec;
 | 
				
			||||||
 | 
					        for (auto row : kern.get_rows()) {
 | 
				
			||||||
 | 
					            vec.reset();
 | 
				
			||||||
 | 
					            vec.reserve(kern.num_vars(), rational(0));
 | 
				
			||||||
 | 
					            for (auto &[coeff, v] : kern.get_row(row)) {
 | 
				
			||||||
 | 
					                vec[v] = rational(coeff);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            out.add_row(vec);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TRACE("gg", {
 | 
				
			||||||
 | 
					            tout << "Computed kernel\n";
 | 
				
			||||||
 | 
					            qmat.display(tout);
 | 
				
			||||||
 | 
					            tout << "\n";
 | 
				
			||||||
 | 
					            kern.display(tout);
 | 
				
			||||||
 | 
					            tout << "\n";
 | 
				
			||||||
 | 
					            tout << "basics: " << basics << "\n";
 | 
				
			||||||
 | 
					            out.display(tout);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return out.num_rows() > 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void collect_statistics(statistics &st) const override {}
 | 
				
			||||||
 | 
					    void reset_statistics() override {}
 | 
				
			||||||
 | 
					    void reset() override {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					spacer_arith_kernel::plugin *mk_simplex_kernel_plugin() {
 | 
				
			||||||
 | 
					    return alloc(simplex_arith_kernel_plugin);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										93
									
								
								src/muz/spacer/spacer_arith_kernel.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/muz/spacer/spacer_arith_kernel.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,93 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/**++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spacer_arith_kernel.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Compute kernel of a matrix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Hari Govind
 | 
				
			||||||
 | 
					    Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "spacer_matrix.h"
 | 
				
			||||||
 | 
					#include "util/statistics.h"
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					   Computes a kernel of a matrix.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					class spacer_arith_kernel {
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    class plugin {
 | 
				
			||||||
 | 
					      public:
 | 
				
			||||||
 | 
					        virtual ~plugin() {}
 | 
				
			||||||
 | 
					        virtual bool compute_kernel(const spacer_matrix &in_matrix,
 | 
				
			||||||
 | 
					                                    spacer_matrix &out_kernel,
 | 
				
			||||||
 | 
					                                    vector<unsigned> &basics) = 0;
 | 
				
			||||||
 | 
					        virtual void collect_statistics(statistics &st) const = 0;
 | 
				
			||||||
 | 
					        virtual void reset_statistics() = 0;
 | 
				
			||||||
 | 
					        virtual void reset() = 0;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected:
 | 
				
			||||||
 | 
					    struct stats {
 | 
				
			||||||
 | 
					        unsigned m_failed;
 | 
				
			||||||
 | 
					        stats() { reset(); }
 | 
				
			||||||
 | 
					        void reset() { m_failed = 0; }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    stats m_st;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Input matrix for which kernel is to be computed
 | 
				
			||||||
 | 
					    const spacer_matrix &m_matrix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Output matrix representing the kernel
 | 
				
			||||||
 | 
					    spacer_matrix m_kernel;
 | 
				
			||||||
 | 
					    /// columns in the kernel that correspond to basic vars
 | 
				
			||||||
 | 
					    vector<unsigned> m_basic_vars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    scoped_ptr<plugin> m_plugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    spacer_arith_kernel(spacer_matrix &matrix)
 | 
				
			||||||
 | 
					        : m_matrix(matrix), m_kernel(0, 0) {}
 | 
				
			||||||
 | 
					    virtual ~spacer_arith_kernel() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void set_plugin(spacer_arith_kernel::plugin *plugin) { m_plugin = plugin; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Computes kernel of a matrix
 | 
				
			||||||
 | 
					    /// returns true if the computation was successful
 | 
				
			||||||
 | 
					    /// use \p spacer_arith_kernel::get_kernel() to get the kernel
 | 
				
			||||||
 | 
					    bool compute_kernel();
 | 
				
			||||||
 | 
					    bool operator()() { return compute_kernel(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const spacer_matrix &get_kernel() const { return m_kernel; }
 | 
				
			||||||
 | 
					    const vector<unsigned> &get_basic_vars() const { return m_basic_vars; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void reset() {
 | 
				
			||||||
 | 
					        m_kernel = spacer_matrix(0, 0);
 | 
				
			||||||
 | 
					        if (m_plugin) m_plugin->reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    virtual void collect_statistics(statistics &st) const {
 | 
				
			||||||
 | 
					        st.update("SPACER arith kernel failed", m_st.m_failed);
 | 
				
			||||||
 | 
					        if (m_plugin) { m_plugin->collect_statistics(st); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    virtual void reset_statistics() {
 | 
				
			||||||
 | 
					        m_st.reset();
 | 
				
			||||||
 | 
					        if (m_plugin) m_plugin->reset_statistics();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					spacer_arith_kernel::plugin *mk_simplex_kernel_plugin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										397
									
								
								src/muz/spacer/spacer_cluster.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								src/muz/spacer/spacer_cluster.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,397 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_cluster.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Discover and mark lemma clusters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/var_subst.h"
 | 
				
			||||||
 | 
					#include "ast/substitution/substitution.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_antiunify.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_cluster.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_manager.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_util.h"
 | 
				
			||||||
 | 
					#include "smt/tactic/unit_subsumption_tactic.h"
 | 
				
			||||||
 | 
					#include "util/mpq.h"
 | 
				
			||||||
 | 
					#include "util/vector.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_CLUSTER_SIZE 5
 | 
				
			||||||
 | 
					#define MAX_CLUSTERS 5
 | 
				
			||||||
 | 
					#define GAS_INIT 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using var_offset = std::pair<unsigned, unsigned>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_cluster::lemma_cluster(const expr_ref &pattern)
 | 
				
			||||||
 | 
					    : m(pattern.get_manager()), m_arith(m), m_bv(m), m_ref_count(0),
 | 
				
			||||||
 | 
					      m_pattern(pattern), m_matcher(m), m_gas(GAS_INIT) {
 | 
				
			||||||
 | 
					    m_num_vars = get_num_vars(m_pattern);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_cluster::lemma_cluster(const lemma_cluster &other)
 | 
				
			||||||
 | 
					    : m(other.get_manager()), m_arith(m), m_bv(m), m_ref_count(0),
 | 
				
			||||||
 | 
					      m_pattern(other.get_pattern()), m_num_vars(other.m_num_vars),
 | 
				
			||||||
 | 
					      m_matcher(m), m_gas(other.get_gas()) {
 | 
				
			||||||
 | 
					    for (const auto &li : other.get_lemmas()) { m_lemma_vec.push_back(li); }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Get a conjunction of all the lemmas in cluster
 | 
				
			||||||
 | 
					void lemma_cluster::get_conj_lemmas(expr_ref &e) const {
 | 
				
			||||||
 | 
					    expr_ref_vector conj(m);
 | 
				
			||||||
 | 
					    for (const auto &lem : get_lemmas()) {
 | 
				
			||||||
 | 
					        conj.push_back(lem.get_lemma()->get_expr());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    e = mk_and(conj);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool lemma_cluster::contains(const lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    for (const auto &li : get_lemmas()) {
 | 
				
			||||||
 | 
					        if (lemma->get_expr() == li.get_lemma()->get_expr()) return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned lemma_cluster::get_min_lvl() {
 | 
				
			||||||
 | 
					    if (m_lemma_vec.empty()) return 0;
 | 
				
			||||||
 | 
					    unsigned lvl = m_lemma_vec[0].get_lemma()->level();
 | 
				
			||||||
 | 
					    for (auto l : m_lemma_vec) { lvl = std::min(lvl, l.get_lemma()->level()); }
 | 
				
			||||||
 | 
					    // if all lemmas are at infinity, use the level of the lowest pob
 | 
				
			||||||
 | 
					    if (is_infty_level(lvl)) {
 | 
				
			||||||
 | 
					        for (auto l : m_lemma_vec) {
 | 
				
			||||||
 | 
					          if (l.get_lemma()->has_pob())
 | 
				
			||||||
 | 
					            lvl = std::min(lvl, l.get_lemma()->get_pob()->level());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return lvl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Checks whether \p e matches the pattern of the cluster
 | 
				
			||||||
 | 
					/// Returns true on success and set \p sub to the corresponding substitution
 | 
				
			||||||
 | 
					bool lemma_cluster::match(const expr_ref &e, substitution &sub) {
 | 
				
			||||||
 | 
					    bool pos;
 | 
				
			||||||
 | 
					    var_offset var;
 | 
				
			||||||
 | 
					    expr_offset r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_matcher.reset();
 | 
				
			||||||
 | 
					    bool is_match = m_matcher(m_pattern, e, sub, pos);
 | 
				
			||||||
 | 
					    if (!(is_match && pos)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned n_binds = sub.get_num_bindings();
 | 
				
			||||||
 | 
					    auto is_numeral = [&](expr *e) {
 | 
				
			||||||
 | 
					        return m_arith.is_numeral(e) || m_bv.is_numeral(e);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    // All the matches should be numerals
 | 
				
			||||||
 | 
					    for (unsigned i = 0; i < n_binds; i++) {
 | 
				
			||||||
 | 
					        sub.get_binding(i, var, r);
 | 
				
			||||||
 | 
					        if (!is_numeral(r.get_expr())) return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool lemma_cluster::can_contain(const lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    substitution sub(m);
 | 
				
			||||||
 | 
					    expr_ref cube(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sub.reserve(1, m_num_vars);
 | 
				
			||||||
 | 
					    cube = mk_and(lemma->get_cube());
 | 
				
			||||||
 | 
					    normalize_order(cube, cube);
 | 
				
			||||||
 | 
					    return match(cube, sub);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_cluster::lemma_info *
 | 
				
			||||||
 | 
					lemma_cluster::get_lemma_info(const lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    SASSERT(contains(lemma));
 | 
				
			||||||
 | 
					    for (auto &li : m_lemma_vec) {
 | 
				
			||||||
 | 
					        if (lemma == li.get_lemma()) { return &li; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    UNREACHABLE();
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Removes subsumed lemmas in the cluster
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Removed lemmas are placed into \p removed_lemmas
 | 
				
			||||||
 | 
					void lemma_cluster::rm_subsumed(lemma_info_vector &removed_lemmas) {
 | 
				
			||||||
 | 
					    removed_lemmas.reset();
 | 
				
			||||||
 | 
					    if (m_lemma_vec.size() <= 1) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // set up and run the simplifier
 | 
				
			||||||
 | 
					    tactic_ref simplifier = mk_unit_subsumption_tactic(m);
 | 
				
			||||||
 | 
					    goal_ref g(alloc(goal, m, false, false, false));
 | 
				
			||||||
 | 
					    goal_ref_buffer result;
 | 
				
			||||||
 | 
					    for (auto l : m_lemma_vec) { g->assert_expr(l.get_lemma()->get_expr()); }
 | 
				
			||||||
 | 
					    (*simplifier)(g, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SASSERT(result.size() == 1);
 | 
				
			||||||
 | 
					    goal *r = result[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // nothing removed
 | 
				
			||||||
 | 
					    if (r->size() == m_lemma_vec.size()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // collect removed lemmas
 | 
				
			||||||
 | 
					    lemma_info_vector keep;
 | 
				
			||||||
 | 
					    for (auto lem : m_lemma_vec) {
 | 
				
			||||||
 | 
					        bool found = false;
 | 
				
			||||||
 | 
					        for (unsigned i = 0; i < r->size(); i++) {
 | 
				
			||||||
 | 
					            if (lem.get_lemma()->get_expr() == r->form(i)) {
 | 
				
			||||||
 | 
					                found = true;
 | 
				
			||||||
 | 
					                keep.push_back(lem);
 | 
				
			||||||
 | 
					                TRACE("cluster_stats_verb", tout << "Keeping lemma "
 | 
				
			||||||
 | 
					                                                 << lem.get_lemma()->get_cube()
 | 
				
			||||||
 | 
					                                                 << "\n";);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!found) {
 | 
				
			||||||
 | 
					            TRACE("cluster_stats_verb", tout << "Removing subsumed lemma "
 | 
				
			||||||
 | 
					                                             << lem.get_lemma()->get_cube()
 | 
				
			||||||
 | 
					                                             << "\n";);
 | 
				
			||||||
 | 
					            removed_lemmas.push_back(lem);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    m_lemma_vec.reset();
 | 
				
			||||||
 | 
					    m_lemma_vec.append(keep);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A a lemma to a cluster
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Removes subsumed lemmas if \p subs_check is true
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Returns false if lemma does not match the pattern or if it is already in the
 | 
				
			||||||
 | 
					/// cluster. Repetition of lemmas is avoided by doing a linear scan over the
 | 
				
			||||||
 | 
					/// lemmas in the cluster. Adding a lemma can reduce the size of the cluster due
 | 
				
			||||||
 | 
					/// to subsumption reduction.
 | 
				
			||||||
 | 
					bool lemma_cluster::add_lemma(const lemma_ref &lemma, bool subsume) {
 | 
				
			||||||
 | 
					    substitution sub(m);
 | 
				
			||||||
 | 
					    expr_ref cube(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sub.reserve(1, m_num_vars);
 | 
				
			||||||
 | 
					    cube = mk_and(lemma->get_cube());
 | 
				
			||||||
 | 
					    normalize_order(cube, cube);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!match(cube, sub)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // cluster already contains the lemma
 | 
				
			||||||
 | 
					    if (contains(lemma)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("cluster_stats_verb",
 | 
				
			||||||
 | 
					          tout << "Trying to add lemma " << lemma->get_cube() << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lemma_cluster::lemma_info li(lemma, sub);
 | 
				
			||||||
 | 
					    m_lemma_vec.push_back(li);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (subsume) {
 | 
				
			||||||
 | 
					        lemma_info_vector removed_lemmas;
 | 
				
			||||||
 | 
					        rm_subsumed(removed_lemmas);
 | 
				
			||||||
 | 
					        for (auto rm : removed_lemmas) {
 | 
				
			||||||
 | 
					            // There is going to at most one removed lemma that matches l_i
 | 
				
			||||||
 | 
					            // if there is one, return false since the new lemma was not added
 | 
				
			||||||
 | 
					            if (rm.get_lemma() == li.get_lemma()) return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    TRACE("cluster_stats", tout << "Added lemma\n" << mk_and(lemma->get_cube()) << "\n"
 | 
				
			||||||
 | 
					                                << "to  existing cluster\n" << m_pattern << "\n";);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_cluster_finder::lemma_cluster_finder(ast_manager &_m)
 | 
				
			||||||
 | 
					    : m(_m), m_arith(m), m_bv(m) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check whether \p cube and \p lcube differ only in interpreted constants
 | 
				
			||||||
 | 
					bool lemma_cluster_finder::are_neighbours(const expr_ref &cube1,
 | 
				
			||||||
 | 
					                                          const expr_ref &cube2) {
 | 
				
			||||||
 | 
					    SASSERT(is_ground(cube1));
 | 
				
			||||||
 | 
					    SASSERT(is_ground(cube2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    anti_unifier antiunify(m);
 | 
				
			||||||
 | 
					    expr_ref pat(m);
 | 
				
			||||||
 | 
					    substitution sub1(m), sub2(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    antiunify(cube1, cube2, pat, sub1, sub2);
 | 
				
			||||||
 | 
					    SASSERT(sub1.get_num_bindings() == sub2.get_num_bindings());
 | 
				
			||||||
 | 
					    return is_numeric_sub(sub1) && is_numeric_sub(sub2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Compute antiunification of \p cube with all formulas in \p fmls.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Should return
 | 
				
			||||||
 | 
					///         \exist res (\forall f \in fmls (\exist i_sub res[i_sub] == f))
 | 
				
			||||||
 | 
					/// However, the algorithm is incomplete: it returns such a res iff
 | 
				
			||||||
 | 
					///         res \in {antiU(cube,  e) | e \in fmls}
 | 
				
			||||||
 | 
					/// Returns true if res is found
 | 
				
			||||||
 | 
					/// TODO: do complete n-ary anti-unification. Not done now
 | 
				
			||||||
 | 
					/// because anti_unifier does not support free variables
 | 
				
			||||||
 | 
					bool lemma_cluster_finder::anti_unify_n_intrp(const expr_ref &cube,
 | 
				
			||||||
 | 
					                                              expr_ref_vector &fmls,
 | 
				
			||||||
 | 
					                                              expr_ref &res) {
 | 
				
			||||||
 | 
					    expr_ref_vector patterns(m);
 | 
				
			||||||
 | 
					    expr_ref pat(m);
 | 
				
			||||||
 | 
					    anti_unifier antiunify(m);
 | 
				
			||||||
 | 
					    substitution sub1(m), sub2(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("cluster_stats_verb",
 | 
				
			||||||
 | 
					          tout << "Trying to generate a general pattern for " << cube
 | 
				
			||||||
 | 
					               << " neighbours are " << fmls << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // collect candidates for res
 | 
				
			||||||
 | 
					    for (expr *c : fmls) {
 | 
				
			||||||
 | 
					        antiunify.reset();
 | 
				
			||||||
 | 
					        sub1.reset();
 | 
				
			||||||
 | 
					        sub2.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SASSERT(are_neighbours(cube, {c, m}));
 | 
				
			||||||
 | 
					        antiunify(cube, expr_ref(c, m), pat, sub1, sub2);
 | 
				
			||||||
 | 
					        patterns.push_back(pat);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // go through all the patterns to see if there is a pattern which is general
 | 
				
			||||||
 | 
					    // enough to include all lemmas.
 | 
				
			||||||
 | 
					    bool is_general_pattern = false, pos = true, all_same = true;
 | 
				
			||||||
 | 
					    sem_matcher matcher(m);
 | 
				
			||||||
 | 
					    unsigned n_vars_pat = 0;
 | 
				
			||||||
 | 
					    for (expr *e : patterns) {
 | 
				
			||||||
 | 
					        TRACE("cluster_stats_verb",
 | 
				
			||||||
 | 
					              tout << "Checking pattern " << mk_pp(e, m) << "\n";);
 | 
				
			||||||
 | 
					        is_general_pattern = true;
 | 
				
			||||||
 | 
					        n_vars_pat = get_num_vars(e);
 | 
				
			||||||
 | 
					        all_same = all_same && n_vars_pat == 0;
 | 
				
			||||||
 | 
					        for (auto *lcube : fmls) {
 | 
				
			||||||
 | 
					            matcher.reset();
 | 
				
			||||||
 | 
					            sub1.reset();
 | 
				
			||||||
 | 
					            sub1.reserve(1, n_vars_pat);
 | 
				
			||||||
 | 
					            if (!(matcher(e, lcube, sub1, pos) && pos)) {
 | 
				
			||||||
 | 
					                // this pattern is no good
 | 
				
			||||||
 | 
					                is_general_pattern = false;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (is_general_pattern) {
 | 
				
			||||||
 | 
					            SASSERT(e != nullptr);
 | 
				
			||||||
 | 
					            TRACE("cluster_stats",
 | 
				
			||||||
 | 
					                  tout << "Found a general pattern\n" << mk_pp(e, m) << "\n";);
 | 
				
			||||||
 | 
					            // found a good pattern
 | 
				
			||||||
 | 
					            res = expr_ref(e, m);
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CTRACE("cluster_stats", !all_same,
 | 
				
			||||||
 | 
					           tout << "Failed to find a general pattern for cluster. Cube is: "
 | 
				
			||||||
 | 
					                << cube << " Patterns are " << patterns << "\n";);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Add a new lemma \p lemma to a cluster
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Creates a new cluster for the lemma if necessary
 | 
				
			||||||
 | 
					void lemma_cluster_finder::cluster(lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    scoped_watch _w_(m_st.watch);
 | 
				
			||||||
 | 
					    pred_transformer &pt = (lemma->get_pob())->pt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // check whether lemmas has already been added
 | 
				
			||||||
 | 
					    if (pt.clstr_contains(lemma)) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Add the lemma to a cluster it is matched against
 | 
				
			||||||
 | 
					    lemma_cluster *clstr = pt.clstr_match(lemma);
 | 
				
			||||||
 | 
					    if (clstr && clstr->get_size() <= MAX_CLUSTER_SIZE) {
 | 
				
			||||||
 | 
					        TRACE("cluster_stats_verb", {
 | 
				
			||||||
 | 
					            tout << "Trying to add lemma\n" << lemma->get_cube()
 | 
				
			||||||
 | 
					                 << " to an existing cluster\n";
 | 
				
			||||||
 | 
					            for (auto lem : clstr->get_lemmas())
 | 
				
			||||||
 | 
					                tout << lem.get_lemma()->get_cube() << "\n";
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        clstr->add_lemma(lemma);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Dont create more than MAX_CLUSTERS number of clusters
 | 
				
			||||||
 | 
					    if (clstr && pt.clstr_count(clstr->get_pattern()) > MAX_CLUSTERS) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Try to create a new cluster with lemma even if it can belong to an
 | 
				
			||||||
 | 
					    // oversized cluster. The new cluster will not contain any lemma that is
 | 
				
			||||||
 | 
					    // already in another cluster.
 | 
				
			||||||
 | 
					    lemma_ref_vector all_lemmas;
 | 
				
			||||||
 | 
					    pt.get_all_lemmas(all_lemmas, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref lcube(m), cube(m);
 | 
				
			||||||
 | 
					    lcube = mk_and(lemma->get_cube());
 | 
				
			||||||
 | 
					    normalize_order(lcube, lcube);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector lma_cubes(m);
 | 
				
			||||||
 | 
					    lemma_ref_vector neighbours;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto *l : all_lemmas) {
 | 
				
			||||||
 | 
					        cube.reset();
 | 
				
			||||||
 | 
					        cube = mk_and(l->get_cube());
 | 
				
			||||||
 | 
					        normalize_order(cube, cube);
 | 
				
			||||||
 | 
					        // make sure that l is not in any other clusters
 | 
				
			||||||
 | 
					        if (are_neighbours(lcube, cube) && cube != lcube &&
 | 
				
			||||||
 | 
					            !pt.clstr_contains(l)) {
 | 
				
			||||||
 | 
					            neighbours.push_back(l);
 | 
				
			||||||
 | 
					            lma_cubes.push_back(cube);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (neighbours.empty()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // compute the most general pattern to which lemmas fit
 | 
				
			||||||
 | 
					    expr_ref pattern(m);
 | 
				
			||||||
 | 
					    bool is_cluster = anti_unify_n_intrp(lcube, lma_cubes, pattern);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // no general pattern
 | 
				
			||||||
 | 
					    if (!is_cluster || get_num_vars(pattern) == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // When creating a cluster, its size can be more than MAX_CLUSTER_SIZE. The
 | 
				
			||||||
 | 
					    // size limitation is only for adding new lemmas to the cluster. The size is
 | 
				
			||||||
 | 
					    // just an arbitrary number.
 | 
				
			||||||
 | 
					    // What matters is that we do not allow a cluster to grow indefinitely.
 | 
				
			||||||
 | 
					    // for example, given a cluster in which one lemma subsumes all other
 | 
				
			||||||
 | 
					    // lemmas. No matter how big the cluster is, GSpacer is going to produce the
 | 
				
			||||||
 | 
					    // exact same pob on this cluster. This can lead to divergence. The
 | 
				
			||||||
 | 
					    // subsumption check we do is based on unit propagation, it is not complete.
 | 
				
			||||||
 | 
					    lemma_cluster *cluster = pt.mk_cluster(pattern);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("cluster_stats",
 | 
				
			||||||
 | 
					          tout << "created new cluster with pattern:\n" << pattern << "\n"
 | 
				
			||||||
 | 
					               << " and lemma cube:\n" << lcube << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF_VERBOSE(2, verbose_stream() << "\ncreated new cluster with pattern: "
 | 
				
			||||||
 | 
					                                   << pattern << "\n"
 | 
				
			||||||
 | 
					                                   << " and lemma cube: " << lcube << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const lemma_ref &l : neighbours) {
 | 
				
			||||||
 | 
					        SASSERT(cluster->can_contain(l));
 | 
				
			||||||
 | 
					        bool added = cluster->add_lemma(l, false);
 | 
				
			||||||
 | 
					        CTRACE("cluster_stats", added,
 | 
				
			||||||
 | 
					               tout << "Added neighbour lemma\n" << mk_and(l->get_cube()) << "\n";);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // finally add the lemma and do subsumption check
 | 
				
			||||||
 | 
					    cluster->add_lemma(lemma, true);
 | 
				
			||||||
 | 
					    SASSERT(cluster->get_size() >= 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_cluster_finder::collect_statistics(statistics &st) const {
 | 
				
			||||||
 | 
					    st.update("time.spacer.solve.reach.cluster", m_st.watch.get_seconds());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										176
									
								
								src/muz/spacer/spacer_cluster.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/muz/spacer/spacer_cluster.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,176 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_cluster.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Discover and mark lemma clusters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ast/ast_util.h>
 | 
				
			||||||
 | 
					#include <ast/substitution/substitution.h>
 | 
				
			||||||
 | 
					#include <muz/spacer/spacer_sem_matcher.h>
 | 
				
			||||||
 | 
					#include <util/ref_vector.h>
 | 
				
			||||||
 | 
					#include <util/statistics.h>
 | 
				
			||||||
 | 
					#include <util/stopwatch.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GAS_POB_COEFF 5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					class lemma;
 | 
				
			||||||
 | 
					using lemma_ref = ref<lemma>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Representation of a cluster of lemmas
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// A cluster of lemmas is a collection of lemma instances. A cluster is
 | 
				
			||||||
 | 
					/// defined by a \p pattern that is a qff formula with free variables, and
 | 
				
			||||||
 | 
					/// contains lemmas that are instances of the pattern (i.e., obtained from the
 | 
				
			||||||
 | 
					/// pattern by substitution of constants for variables). That is, each lemma
 | 
				
			||||||
 | 
					/// in the cluster matches the pattern.
 | 
				
			||||||
 | 
					class lemma_cluster {
 | 
				
			||||||
 | 
					    /// Lemma in a cluster
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// A lemma and a substitution witnessing that lemma is an instance of a
 | 
				
			||||||
 | 
					    /// pattern
 | 
				
			||||||
 | 
					    class lemma_info {
 | 
				
			||||||
 | 
					        // a lemma
 | 
				
			||||||
 | 
					        lemma_ref m_lemma;
 | 
				
			||||||
 | 
					        // a substitution such that for some pattern, \p m_lemma is an instance
 | 
				
			||||||
 | 
					        // substitution is stored in std_order for quantifiers (i.e., reverse of
 | 
				
			||||||
 | 
					        // expected)
 | 
				
			||||||
 | 
					        substitution m_sub;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      public:
 | 
				
			||||||
 | 
					        lemma_info(const lemma_ref &body, const substitution &sub)
 | 
				
			||||||
 | 
					            : m_lemma(body), m_sub(sub) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const lemma_ref &get_lemma() const { return m_lemma; }
 | 
				
			||||||
 | 
					        const substitution &get_sub() const { return m_sub; }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    using lemma_info_vector = vector<lemma_cluster::lemma_info, true>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					    bv_util m_bv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // reference counter
 | 
				
			||||||
 | 
					    unsigned m_ref_count;
 | 
				
			||||||
 | 
					    // pattern defining the cluster
 | 
				
			||||||
 | 
					    expr_ref m_pattern;
 | 
				
			||||||
 | 
					    unsigned m_num_vars;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // vector of lemmas in the cluster
 | 
				
			||||||
 | 
					    lemma_info_vector m_lemma_vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // shared matcher object to match lemmas against the pattern
 | 
				
			||||||
 | 
					    sem_matcher m_matcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The number of times CSM has to be tried using this cluster
 | 
				
			||||||
 | 
					    unsigned m_gas;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Remove subsumed lemmas in the cluster.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns list of removed lemmas in \p removed_lemmas
 | 
				
			||||||
 | 
					    void rm_subsumed(lemma_info_vector &removed_lemmas);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Checks whether \p e matches m_pattern.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns true on success and sets \p sub to the corresponding
 | 
				
			||||||
 | 
					    /// substitution
 | 
				
			||||||
 | 
					    bool match(const expr_ref &e, substitution &sub);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ast_manager &get_manager() const { return m; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    lemma_cluster(const expr_ref &pattern);
 | 
				
			||||||
 | 
					    lemma_cluster(const lemma_cluster &other);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const lemma_info_vector &get_lemmas() const { return m_lemma_vec; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void dec_gas() {
 | 
				
			||||||
 | 
					        if (m_gas > 0) m_gas--;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned get_gas() const { return m_gas; }
 | 
				
			||||||
 | 
					    unsigned get_pob_gas() const { return GAS_POB_COEFF * m_lemma_vec.size(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get a conjunction of all the lemmas in cluster
 | 
				
			||||||
 | 
					    void get_conj_lemmas(expr_ref &e) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Try to add \p lemma to cluster. Remove subsumed lemmas if \p subs_check
 | 
				
			||||||
 | 
					    /// is true
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns false if lemma does not match the pattern or if it is already in
 | 
				
			||||||
 | 
					    /// the cluster Repetition of lemmas is avoided by doing a linear scan over
 | 
				
			||||||
 | 
					    /// the lemmas in the cluster. Adding a lemma can reduce the size of the
 | 
				
			||||||
 | 
					    /// cluster due to subs_check
 | 
				
			||||||
 | 
					    bool add_lemma(const lemma_ref &lemma, bool subsume = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool contains(const lemma_ref &lemma);
 | 
				
			||||||
 | 
					    bool can_contain(const lemma_ref &lemma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Return the minimum level of lemmas in he cluster
 | 
				
			||||||
 | 
					    unsigned get_min_lvl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lemma_cluster::lemma_info *get_lemma_info(const lemma_ref &lemma);
 | 
				
			||||||
 | 
					    unsigned get_size() const { return m_lemma_vec.size(); }
 | 
				
			||||||
 | 
					    const expr_ref &get_pattern() const { return m_pattern; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void inc_ref() { ++m_ref_count; }
 | 
				
			||||||
 | 
					    void dec_ref() {
 | 
				
			||||||
 | 
					        --m_ref_count;
 | 
				
			||||||
 | 
					        if (m_ref_count == 0) { dealloc(this); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class lemma_cluster_finder {
 | 
				
			||||||
 | 
					    struct stats {
 | 
				
			||||||
 | 
					        unsigned max_group_size;
 | 
				
			||||||
 | 
					        stopwatch watch;
 | 
				
			||||||
 | 
					        stats() { reset(); }
 | 
				
			||||||
 | 
					        void reset() {
 | 
				
			||||||
 | 
					            max_group_size = 0;
 | 
				
			||||||
 | 
					            watch.reset();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    stats m_st;
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					    bv_util m_bv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check whether \p cube and \p lcube differ only in interpreted constants
 | 
				
			||||||
 | 
					    bool are_neighbours(const expr_ref &cube, const expr_ref &lcube);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// N-ary antiunify
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns whether there is a substitution with only interpreted consts
 | 
				
			||||||
 | 
					    bool anti_unify_n_intrp(const expr_ref &cube, expr_ref_vector &fmls,
 | 
				
			||||||
 | 
					                            expr_ref &res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    lemma_cluster_finder(ast_manager &m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Add a new lemma \p lemma to a cluster
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Creates a new cluster for the lemma if necessary
 | 
				
			||||||
 | 
					    void cluster(lemma_ref &lemma);
 | 
				
			||||||
 | 
					    void collect_statistics(statistics &st) const;
 | 
				
			||||||
 | 
					    void reset_statistics() { m_st.reset(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using lemma_info_vector = lemma_cluster::lemma_info_vector;
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										240
									
								
								src/muz/spacer/spacer_cluster_util.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								src/muz/spacer/spacer_cluster_util.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,240 @@
 | 
				
			||||||
 | 
					/**++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spacer_cluster_util.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Utility methods for clustering
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Hari Govind
 | 
				
			||||||
 | 
					    Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "ast/ast.h"
 | 
				
			||||||
 | 
					#include "ast/ast_pp.h"
 | 
				
			||||||
 | 
					#include "ast/for_each_expr.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/rewriter.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/rewriter_def.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/th_rewriter.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					/// Arithmetic term order
 | 
				
			||||||
 | 
					struct arith_add_less_proc {
 | 
				
			||||||
 | 
					    const arith_util &m_arith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    arith_add_less_proc(const arith_util &arith) : m_arith(arith) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool operator()(expr *e1, expr *e2) const {
 | 
				
			||||||
 | 
					        if (e1 == e2) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ast_lt_proc ast_lt;
 | 
				
			||||||
 | 
					        expr *k1 = nullptr, *t1 = nullptr, *k2 = nullptr, *t2 = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // k1*t1 < k2*t2 iff  t1 < t2 or  t1 == t2 && k1 < k2
 | 
				
			||||||
 | 
					        // k1 and k2 can be null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!m_arith.is_mul(e1, k1, t1)) { t1 = e1; }
 | 
				
			||||||
 | 
					        if (!m_arith.is_mul(e2, k2, t2)) { t2 = e2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SASSERT(t1 && t2);
 | 
				
			||||||
 | 
					        if (t1 != t2) return ast_lt(t1, t2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //  here: t1 == t2 && k1 != k2
 | 
				
			||||||
 | 
					        SASSERT(k1 != k2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // check for null
 | 
				
			||||||
 | 
					        if (!k1 || !k2) return !k1;
 | 
				
			||||||
 | 
					        return ast_lt(k1, k2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct bool_and_less_proc {
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    const arith_util &m_arith;
 | 
				
			||||||
 | 
					    bool_and_less_proc(ast_manager &mgr, const arith_util &arith)
 | 
				
			||||||
 | 
					        : m(mgr), m_arith(arith) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool operator()(expr *e1, expr *e2) const {
 | 
				
			||||||
 | 
					        expr *a1 = nullptr, *a2 = nullptr;
 | 
				
			||||||
 | 
					        bool is_not1, is_not2;
 | 
				
			||||||
 | 
					        if (e1 == e2) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        is_not1 = m.is_not(e1, a1);
 | 
				
			||||||
 | 
					        a1 = is_not1 ? a1 : e1;
 | 
				
			||||||
 | 
					        is_not2 = m.is_not(e2, a2);
 | 
				
			||||||
 | 
					        a2 = is_not2 ? a2 : e2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return a1 == a2 ? is_not1 < is_not2 : arith_lt(a1, a2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool arith_lt(expr *e1, expr *e2) const {
 | 
				
			||||||
 | 
					        ast_lt_proc ast_lt;
 | 
				
			||||||
 | 
					        expr *t1, *k1, *t2, *k2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (e1 == e2) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (e1->get_kind() != e2->get_kind()) return e1->get_kind() < e2->get_kind();
 | 
				
			||||||
 | 
					        if (!is_app(e1)) return ast_lt(e1, e2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app *a1 = to_app(e1), *a2 = to_app(e2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (a1->get_family_id() != a2->get_family_id())
 | 
				
			||||||
 | 
					            return a1->get_family_id() < a2->get_family_id();
 | 
				
			||||||
 | 
					        if (a1->get_decl_kind() != a2->get_decl_kind())
 | 
				
			||||||
 | 
					            return a1->get_decl_kind() < a2->get_decl_kind();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!(m_arith.is_le(e1, t1, k1) || m_arith.is_lt(e1, t1, k1) ||
 | 
				
			||||||
 | 
					              m_arith.is_ge(e1, t1, k1) || m_arith.is_gt(e1, t1, k1))) {
 | 
				
			||||||
 | 
					            t1 = e1;
 | 
				
			||||||
 | 
					            k1 = nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!(m_arith.is_le(e2, t2, k2) || m_arith.is_lt(e2, t2, k2) ||
 | 
				
			||||||
 | 
					              m_arith.is_ge(e2, t2, k2) || m_arith.is_gt(e2, t2, k2))) {
 | 
				
			||||||
 | 
					            t2 = e2;
 | 
				
			||||||
 | 
					            k2 = nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!k1 || !k2) { return k1 == k2 ? ast_lt(t1, t2) : k1 < k2; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (t1 == t2) return ast_lt(k1, k2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (t1->get_kind() != t2->get_kind())
 | 
				
			||||||
 | 
					          return t1->get_kind() < t2->get_kind();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!is_app(t1)) return ast_lt(t1, t2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned d1 = to_app(t1)->get_depth();
 | 
				
			||||||
 | 
					        unsigned d2 = to_app(t2)->get_depth();
 | 
				
			||||||
 | 
					        if (d1 != d2) return d1 < d2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // AG: order by the leading uninterpreted constant
 | 
				
			||||||
 | 
					        expr *u1 = nullptr, *u2 = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        u1 = get_first_uc(t1);
 | 
				
			||||||
 | 
					        u2 = get_first_uc(t2);
 | 
				
			||||||
 | 
					        if (!u1 || !u2) { return u1 == u2 ? ast_lt(t1, t2) : u1 < u2; }
 | 
				
			||||||
 | 
					        return u1 == u2 ? ast_lt(t1, t2) : ast_lt(u1, u2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns first in left-most traversal uninterpreted constant of \p e
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns null when no uninterpreted constant is found.
 | 
				
			||||||
 | 
					    /// Recursive, assumes that expression is shallow and recursion is bounded.
 | 
				
			||||||
 | 
					    expr *get_first_uc(expr *e) const {
 | 
				
			||||||
 | 
					        expr *t, *k;
 | 
				
			||||||
 | 
					        if (is_uninterp_const(e))
 | 
				
			||||||
 | 
					            return e;
 | 
				
			||||||
 | 
					        else if (m_arith.is_add(e)) {
 | 
				
			||||||
 | 
					            if (to_app(e)->get_num_args() == 0) return nullptr;
 | 
				
			||||||
 | 
					            expr *a1 = to_app(e)->get_arg(0);
 | 
				
			||||||
 | 
					            // HG: for 3 + a, returns nullptr
 | 
				
			||||||
 | 
					            return get_first_uc(a1);
 | 
				
			||||||
 | 
					        } else if (m_arith.is_mul(e, k, t)) {
 | 
				
			||||||
 | 
					            return get_first_uc(t);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Rewriter for normalize_order()
 | 
				
			||||||
 | 
					struct term_ordered_rpp : public default_rewriter_cfg {
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					    arith_add_less_proc m_add_less;
 | 
				
			||||||
 | 
					    bool_and_less_proc m_and_less;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    term_ordered_rpp(ast_manager &man)
 | 
				
			||||||
 | 
					        : m(man), m_arith(m), m_add_less(m_arith), m_and_less(m, m_arith) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool is_add(func_decl const *n) const {
 | 
				
			||||||
 | 
					        return is_decl_of(n, m_arith.get_family_id(), OP_ADD);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    br_status reduce_app(func_decl *f, unsigned num, expr *const *args,
 | 
				
			||||||
 | 
					                         expr_ref &result, proof_ref &result_pr) {
 | 
				
			||||||
 | 
					        br_status st = BR_FAILED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (is_add(f)) {
 | 
				
			||||||
 | 
					            ptr_buffer<expr> kids;
 | 
				
			||||||
 | 
					            kids.append(num, args);
 | 
				
			||||||
 | 
					            std::stable_sort(kids.data(), kids.data() + kids.size(),
 | 
				
			||||||
 | 
					                             m_add_less);
 | 
				
			||||||
 | 
					            result = m_arith.mk_add(num, kids.data());
 | 
				
			||||||
 | 
					            return BR_DONE;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (m.is_and(f)) {
 | 
				
			||||||
 | 
					            ptr_buffer<expr> kids;
 | 
				
			||||||
 | 
					            kids.append(num, args);
 | 
				
			||||||
 | 
					            std::stable_sort(kids.data(), kids.data() + kids.size(),
 | 
				
			||||||
 | 
					                             m_and_less);
 | 
				
			||||||
 | 
					            result = m.mk_and(num, kids.data());
 | 
				
			||||||
 | 
					            return BR_DONE;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return st;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Normalize an arithmetic expression using term order
 | 
				
			||||||
 | 
					void normalize_order(expr *e, expr_ref &out) {
 | 
				
			||||||
 | 
					    params_ref params;
 | 
				
			||||||
 | 
					    // -- arith_rewriter params
 | 
				
			||||||
 | 
					    params.set_bool("sort_sums", true);
 | 
				
			||||||
 | 
					    // params.set_bool("gcd_rounding", true);
 | 
				
			||||||
 | 
					    // params.set_bool("arith_lhs", true);
 | 
				
			||||||
 | 
					    // -- poly_rewriter params
 | 
				
			||||||
 | 
					    // params.set_bool("som", true);
 | 
				
			||||||
 | 
					    // params.set_bool("flat", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // apply theory rewriter
 | 
				
			||||||
 | 
					    th_rewriter rw1(out.m(), params);
 | 
				
			||||||
 | 
					    rw1(e, out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    STRACE("spacer_normalize_order'",
 | 
				
			||||||
 | 
					           tout << "OUT Before:" << mk_pp(out, out.m()) << "\n";);
 | 
				
			||||||
 | 
					    // apply term ordering
 | 
				
			||||||
 | 
					    term_ordered_rpp t_ordered(out.m());
 | 
				
			||||||
 | 
					    rewriter_tpl<term_ordered_rpp> rw2(out.m(), false, t_ordered);
 | 
				
			||||||
 | 
					    rw2(out.get(), out);
 | 
				
			||||||
 | 
					    STRACE("spacer_normalize_order'",
 | 
				
			||||||
 | 
					           tout << "OUT After :" << mk_pp(out, out.m()) << "\n";);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Multiply an expression \p fml by a rational \p num
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// \p fml should be of sort Int, Real, or BitVec
 | 
				
			||||||
 | 
					/// multiplication is simplifying
 | 
				
			||||||
 | 
					void mul_by_rat(expr_ref &fml, rational num) {
 | 
				
			||||||
 | 
					    if (num.is_one()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ast_manager &m = fml.get_manager();
 | 
				
			||||||
 | 
					    arith_util m_arith(m);
 | 
				
			||||||
 | 
					    bv_util m_bv(m);
 | 
				
			||||||
 | 
					    expr_ref e(m);
 | 
				
			||||||
 | 
					    SASSERT(m_arith.is_int_real(fml) || m_bv.is_bv(fml));
 | 
				
			||||||
 | 
					    if (m_arith.is_int_real(fml)) {
 | 
				
			||||||
 | 
					        e = m_arith.mk_mul(m_arith.mk_numeral(num, m_arith.is_int(fml)), fml);
 | 
				
			||||||
 | 
					    } else if (m_bv.is_bv(fml)) {
 | 
				
			||||||
 | 
					        unsigned sz = m_bv.get_bv_size(fml);
 | 
				
			||||||
 | 
					        e = m_bv.mk_bv_mul(m_bv.mk_numeral(num, sz), fml);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // use theory rewriter to simplify
 | 
				
			||||||
 | 
					    params_ref params;
 | 
				
			||||||
 | 
					    params.set_bool("som", true);
 | 
				
			||||||
 | 
					    params.set_bool("flat", true);
 | 
				
			||||||
 | 
					    th_rewriter rw(m, params);
 | 
				
			||||||
 | 
					    rw(e, fml);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
 | 
					template class rewriter_tpl<spacer::term_ordered_rpp>;
 | 
				
			||||||
							
								
								
									
										208
									
								
								src/muz/spacer/spacer_concretize.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								src/muz/spacer/spacer_concretize.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,208 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_concretize.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Concretize a pob
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					#include "spacer_concretize.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace pattern_var_marker_ns {
 | 
				
			||||||
 | 
					struct proc {
 | 
				
			||||||
 | 
					    arith_util &m_arith;
 | 
				
			||||||
 | 
					    expr_fast_mark2 &m_marks;
 | 
				
			||||||
 | 
					    proc(arith_util &arith, expr_fast_mark2 &marks)
 | 
				
			||||||
 | 
					        : m_arith(arith), m_marks(marks) {}
 | 
				
			||||||
 | 
					    void operator()(var *n) const {}
 | 
				
			||||||
 | 
					    void operator()(quantifier *q) const {}
 | 
				
			||||||
 | 
					    void operator()(app const *n) const {
 | 
				
			||||||
 | 
					        expr *e1, *e2;
 | 
				
			||||||
 | 
					        if (m_arith.is_mul(n, e1, e2)) {
 | 
				
			||||||
 | 
					            if (is_var(e1) && !is_var(e2))
 | 
				
			||||||
 | 
					                m_marks.mark(e2);
 | 
				
			||||||
 | 
					            else if (is_var(e2) && !is_var(e1))
 | 
				
			||||||
 | 
					                m_marks.mark(e1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					}; // namespace pattern_var_marker_ns
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					void pob_concretizer::mark_pattern_vars() {
 | 
				
			||||||
 | 
					    pattern_var_marker_ns::proc proc(m_arith, m_var_marks);
 | 
				
			||||||
 | 
					    quick_for_each_expr(proc, const_cast<expr *>(m_pattern));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool pob_concretizer::push_out(expr_ref_vector &out, const expr_ref &e) {
 | 
				
			||||||
 | 
					    // using m_var_marks to mark both variables and expressions sent to out
 | 
				
			||||||
 | 
					    // the two sets are distinct so we can reuse the same marks
 | 
				
			||||||
 | 
					    if (!m_var_marks.is_marked(e)) {
 | 
				
			||||||
 | 
					        m_var_marks.mark(e);
 | 
				
			||||||
 | 
					        out.push_back(e);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool pob_concretizer::apply(const expr_ref_vector &cube, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    // mark variables that are being split out
 | 
				
			||||||
 | 
					    mark_pattern_vars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto *lit : cube) {
 | 
				
			||||||
 | 
					        if (!apply_lit(lit, out)) {
 | 
				
			||||||
 | 
					            out.reset();
 | 
				
			||||||
 | 
					            m_var_marks.reset();
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_var_marks.reset();
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool pob_concretizer::is_split_var(expr *e, expr *&var, bool &pos) {
 | 
				
			||||||
 | 
					    expr *e1, *e2;
 | 
				
			||||||
 | 
					    rational n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_var_marks.is_marked(e)) {
 | 
				
			||||||
 | 
					        var = e;
 | 
				
			||||||
 | 
					        pos = true;
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    } else if (m_arith.is_mul(e, e1, e2) && m_arith.is_numeral(e1, n) &&
 | 
				
			||||||
 | 
					               m_var_marks.is_marked(e2)) {
 | 
				
			||||||
 | 
					        var = e2;
 | 
				
			||||||
 | 
					        pos = !n.is_neg();
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void pob_concretizer::split_lit_le_lt(expr *_lit, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    expr *e1, *e2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr *lit = _lit;
 | 
				
			||||||
 | 
					    m.is_not(_lit, lit);
 | 
				
			||||||
 | 
					    VERIFY(m_arith.is_le(lit, e1, e2) || m_arith.is_gt(lit, e1, e2) ||
 | 
				
			||||||
 | 
					           m_arith.is_lt(lit, e1, e2) || m_arith.is_ge(lit, e1, e2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ptr_buffer<expr> kids;
 | 
				
			||||||
 | 
					    expr *var;
 | 
				
			||||||
 | 
					    bool pos;
 | 
				
			||||||
 | 
					    expr_ref val(m);
 | 
				
			||||||
 | 
					    for (auto *arg : *to_app(e1)) {
 | 
				
			||||||
 | 
					        if (is_split_var(arg, var, pos)) {
 | 
				
			||||||
 | 
					            val = m_model->operator()(var);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // reuse val to keep the new literal
 | 
				
			||||||
 | 
					            val = pos ? m_arith.mk_le(var, val) : m_arith.mk_ge(var, val);
 | 
				
			||||||
 | 
					            push_out(out, val);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            kids.push_back(arg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (kids.empty()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- nothing was changed in the literal, move it out as is
 | 
				
			||||||
 | 
					    if (kids.size() == to_app(e1)->get_num_args()) {
 | 
				
			||||||
 | 
					        push_out(out, {_lit, m});
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create new leftover literal using remaining arguments
 | 
				
			||||||
 | 
					    expr_ref lhs(m);
 | 
				
			||||||
 | 
					    if (kids.size() == 1) {
 | 
				
			||||||
 | 
					        lhs = kids.get(0);
 | 
				
			||||||
 | 
					    } else
 | 
				
			||||||
 | 
					        lhs = m_arith.mk_add(kids.size(), kids.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref rhs = m_model->operator()(lhs);
 | 
				
			||||||
 | 
					    expr_ref new_lit(m_arith.mk_le(lhs, rhs), m);
 | 
				
			||||||
 | 
					    push_out(out, new_lit);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void pob_concretizer::split_lit_ge_gt(expr *_lit, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    expr *e1, *e2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr *lit = _lit;
 | 
				
			||||||
 | 
					    m.is_not(_lit, lit);
 | 
				
			||||||
 | 
					    VERIFY(m_arith.is_le(lit, e1, e2) || m_arith.is_gt(lit, e1, e2) ||
 | 
				
			||||||
 | 
					           m_arith.is_lt(lit, e1, e2) || m_arith.is_ge(lit, e1, e2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ptr_buffer<expr> kids;
 | 
				
			||||||
 | 
					    expr *var;
 | 
				
			||||||
 | 
					    bool pos;
 | 
				
			||||||
 | 
					    expr_ref val(m);
 | 
				
			||||||
 | 
					    for (auto *arg : *to_app(e1)) {
 | 
				
			||||||
 | 
					        if (is_split_var(arg, var, pos)) {
 | 
				
			||||||
 | 
					            val = m_model->operator()(var);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // reuse val to keep the new literal
 | 
				
			||||||
 | 
					            val = pos ? m_arith.mk_ge(var, val) : m_arith.mk_le(var, val);
 | 
				
			||||||
 | 
					            push_out(out, val);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            kids.push_back(arg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (kids.empty()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- nothing was changed in the literal, move it out as is
 | 
				
			||||||
 | 
					    if (kids.size() == to_app(e1)->get_num_args()) {
 | 
				
			||||||
 | 
					        push_out(out, {_lit, m});
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // create new leftover literal using remaining arguments
 | 
				
			||||||
 | 
					    expr_ref lhs(m);
 | 
				
			||||||
 | 
					    if (kids.size() == 1) {
 | 
				
			||||||
 | 
					        lhs = kids.get(0);
 | 
				
			||||||
 | 
					    } else
 | 
				
			||||||
 | 
					        lhs = m_arith.mk_add(kids.size(), kids.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref rhs = m_model->operator()(lhs);
 | 
				
			||||||
 | 
					    expr_ref new_lit(m_arith.mk_ge(lhs, rhs), m);
 | 
				
			||||||
 | 
					    push_out(out, new_lit);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool pob_concretizer::apply_lit(expr *_lit, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    expr *lit = _lit;
 | 
				
			||||||
 | 
					    bool is_neg = m.is_not(_lit, lit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // split literals of the form a1*x1 + ... + an*xn ~ c, where c is a
 | 
				
			||||||
 | 
					    // constant, ~ is <, <=, >, or >=, and the top level operator of LHS is +
 | 
				
			||||||
 | 
					    expr *e1, *e2;
 | 
				
			||||||
 | 
					    if ((m_arith.is_lt(lit, e1, e2) || m_arith.is_le(lit, e1, e2)) &&
 | 
				
			||||||
 | 
					        m_arith.is_add(e1)) {
 | 
				
			||||||
 | 
					        SASSERT(m_arith.is_numeral(e2));
 | 
				
			||||||
 | 
					        if (!is_neg) {
 | 
				
			||||||
 | 
					            split_lit_le_lt(_lit, out);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            split_lit_ge_gt(_lit, out);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if ((m_arith.is_gt(lit, e1, e2) || m_arith.is_ge(lit, e1, e2)) &&
 | 
				
			||||||
 | 
					               m_arith.is_add(e1)) {
 | 
				
			||||||
 | 
					        SASSERT(m_arith.is_numeral(e2));
 | 
				
			||||||
 | 
					        if (!is_neg) {
 | 
				
			||||||
 | 
					            split_lit_ge_gt(_lit, out);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            split_lit_le_lt(_lit, out);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        out.push_back(_lit);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										77
									
								
								src/muz/spacer/spacer_concretize.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/muz/spacer/spacer_concretize.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_concretize.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Concretize a pob
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/ast.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/expr_safe_replace.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_util.h"
 | 
				
			||||||
 | 
					#include "tactic/core/ctx_simplify_tactic.h"
 | 
				
			||||||
 | 
					#include "util/obj_ref_hashtable.h"
 | 
				
			||||||
 | 
					#include "util/rational.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class pob_concretizer {
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model_ref &m_model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const expr *m_pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_fast_mark2 m_var_marks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Marks all constants to be split in the pattern
 | 
				
			||||||
 | 
					    void mark_pattern_vars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Adds a literal to \p out (unless it is already there)
 | 
				
			||||||
 | 
					    bool push_out(expr_ref_vector &out, const expr_ref &e);
 | 
				
			||||||
 | 
					    // Determines whether \p e is a*var, for some variable in \p m_var_marks
 | 
				
			||||||
 | 
					    // Sets \p pos to sign(a)
 | 
				
			||||||
 | 
					    bool is_split_var(expr *e, expr *&var, bool &pos);
 | 
				
			||||||
 | 
					    // Splits a < or <= literal using the model
 | 
				
			||||||
 | 
					    void split_lit_le_lt(expr *lit, expr_ref_vector &out);
 | 
				
			||||||
 | 
					    // See split_lit_le_lt
 | 
				
			||||||
 | 
					    void split_lit_ge_gt(expr *lit, expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    pob_concretizer(ast_manager &_m, model_ref &model, const expr *pattern)
 | 
				
			||||||
 | 
					        : m(_m), m_arith(m), m_model(model), m_pattern(pattern) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Concretize \p cube into conjunction of simpler literals
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns true on success and adds new literals to out
 | 
				
			||||||
 | 
					    /// ensures: mk_and(out) ==> cube
 | 
				
			||||||
 | 
					    bool apply(expr *cube, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					        expr_ref_vector flat(m);
 | 
				
			||||||
 | 
					        flatten_and(cube, flat);
 | 
				
			||||||
 | 
					        return apply(flat, out);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Concretizes a vector of literals
 | 
				
			||||||
 | 
					    bool apply(const expr_ref_vector &cube, expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Concretizes a single literal
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Returns true on success, new literals are added to \p out
 | 
				
			||||||
 | 
					    bool apply_lit(expr *lit, expr_ref_vector &out);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										98
									
								
								src/muz/spacer/spacer_conjecture.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/muz/spacer/spacer_conjecture.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,98 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					   Copyright (c) 2019 Microsoft Corporation and Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   spacer_conjecture.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Methods to implement conjecture rule in gspacer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Arie Gurfinkel
 | 
				
			||||||
 | 
					   Hari Govind
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/for_each_expr.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					/// Returns true if \p lit is LA/BV inequality with a single free variable
 | 
				
			||||||
 | 
					bool is_mono_var_lit(expr *lit, ast_manager &m) {
 | 
				
			||||||
 | 
					    expr *e;
 | 
				
			||||||
 | 
					    bv_util bv(m);
 | 
				
			||||||
 | 
					    arith_util a_util(m);
 | 
				
			||||||
 | 
					    if (m.is_not(lit, e)) return is_mono_var_lit(e, m);
 | 
				
			||||||
 | 
					    if (a_util.is_arith_expr(lit) || bv.is_bv_ule(lit) ||
 | 
				
			||||||
 | 
					        bv.is_bv_sle(lit)) {
 | 
				
			||||||
 | 
					        return get_num_vars(lit) == 1 && !has_nonlinear_var_mul(lit, m);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns true if \p pattern contains a single mono-var literal
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// That is, \p pattern contains a single suitable literal.
 | 
				
			||||||
 | 
					/// The literal is returned in \p res
 | 
				
			||||||
 | 
					bool find_unique_mono_var_lit(const expr_ref &pattern, expr_ref &res) {
 | 
				
			||||||
 | 
					    if (get_num_vars(pattern) != 1) return false;
 | 
				
			||||||
 | 
					    ast_manager &m = res.m();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // if the pattern has multiple literals, check whether exactly one of them
 | 
				
			||||||
 | 
					    // is leq
 | 
				
			||||||
 | 
					    expr_ref_vector conj(m);
 | 
				
			||||||
 | 
					    conj.push_back(pattern);
 | 
				
			||||||
 | 
					    flatten_and(conj);
 | 
				
			||||||
 | 
					    unsigned count = 0;
 | 
				
			||||||
 | 
					    for (auto *lit : conj) {
 | 
				
			||||||
 | 
					        if (is_mono_var_lit(lit, m)) {
 | 
				
			||||||
 | 
					            if (count) return false;
 | 
				
			||||||
 | 
					            res = lit;
 | 
				
			||||||
 | 
					            count++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    SASSERT(count <= 1);
 | 
				
			||||||
 | 
					    return count == 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Filter out a given literal \p lit from a list of literals
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Returns true if at least one literal was filtered out
 | 
				
			||||||
 | 
					/// \p out contains all the remaining (not filtered) literals
 | 
				
			||||||
 | 
					/// \p out holds the result. Returns true if any literal has been dropped
 | 
				
			||||||
 | 
					bool filter_out_lit(const expr_ref_vector &vec, const expr_ref &lit, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    ast_manager &m = vec.get_manager();
 | 
				
			||||||
 | 
					    bool dirty = false, pos = false;
 | 
				
			||||||
 | 
					    sem_matcher matcher(m);
 | 
				
			||||||
 | 
					    substitution sub(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    out.reset();
 | 
				
			||||||
 | 
					    unsigned lit_num_vars = get_num_vars(lit.get());
 | 
				
			||||||
 | 
					    SASSERT(!(m.is_not(lit) && m.is_eq(to_app(lit)->get_arg(0))));
 | 
				
			||||||
 | 
					    for (auto &c : vec) {
 | 
				
			||||||
 | 
					        sub.reset();
 | 
				
			||||||
 | 
					        sub.reserve(1, lit_num_vars);
 | 
				
			||||||
 | 
					        matcher.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (matcher(lit, c, sub, pos) && pos) {
 | 
				
			||||||
 | 
					            if (is_numeric_sub(sub)) {
 | 
				
			||||||
 | 
					                dirty = true;
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        out.push_back(c);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CTRACE("global", dirty,
 | 
				
			||||||
 | 
					           tout << "Filtered " << lit << " from " << vec << "\n got " << out << "\n";);
 | 
				
			||||||
 | 
					    return dirty;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--*/
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clang-format off
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
#include <iomanip>
 | 
					#include <iomanip>
 | 
				
			||||||
| 
						 | 
					@ -51,30 +52,36 @@ Notes:
 | 
				
			||||||
#include "muz/transforms/dl_mk_rule_inliner.h"
 | 
					#include "muz/transforms/dl_mk_rule_inliner.h"
 | 
				
			||||||
#include "muz/spacer/spacer_qe_project.h"
 | 
					#include "muz/spacer/spacer_qe_project.h"
 | 
				
			||||||
#include "muz/spacer/spacer_sat_answer.h"
 | 
					#include "muz/spacer/spacer_sat_answer.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_concretize.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_global_generalizer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define WEAKNESS_MAX 65535
 | 
					#define WEAKNESS_MAX 65535
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace spacer {
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// pob -- proof obligation
 | 
					/// pob -- proof obligation
 | 
				
			||||||
pob::pob (pob* parent, pred_transformer& pt,
 | 
					pob::pob(pob *parent, pred_transformer &pt, unsigned level, unsigned depth,
 | 
				
			||||||
          unsigned level, unsigned depth, bool add_to_parent):
 | 
					         bool add_to_parent)
 | 
				
			||||||
    m_ref_count (0),
 | 
					    : m_ref_count(0), m_parent(parent), m_pt(pt),
 | 
				
			||||||
    m_parent (parent), m_pt (pt),
 | 
					      m_post(m_pt.get_ast_manager()), m_binding(m_pt.get_ast_manager()),
 | 
				
			||||||
    m_post (m_pt.get_ast_manager ()),
 | 
					      m_new_post(m_pt.get_ast_manager()), m_level(level), m_depth(depth),
 | 
				
			||||||
    m_binding(m_pt.get_ast_manager()),
 | 
					      m_desired_level(0), m_open(true), m_use_farkas(true), m_in_queue(false), m_is_conjecture(false),
 | 
				
			||||||
    m_new_post (m_pt.get_ast_manager ()),
 | 
					      m_enable_local_gen(true), m_enable_concretize(false), m_is_subsume(false),
 | 
				
			||||||
    m_level (level), m_depth (depth),
 | 
					      m_enable_expand_bnd_gen(false), m_weakness(0), m_blocked_lvl(0),
 | 
				
			||||||
    m_open (true), m_use_farkas (true), m_in_queue(false),
 | 
					      m_concretize_pat(m_pt.get_ast_manager()),
 | 
				
			||||||
    m_weakness(0), m_blocked_lvl(0) {
 | 
					      m_gas(0) {
 | 
				
			||||||
    if (add_to_parent && m_parent) {
 | 
					    if (add_to_parent && m_parent) {
 | 
				
			||||||
        m_parent->add_child(*this);
 | 
					        m_parent->add_child(*this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (m_parent) {
 | 
				
			||||||
 | 
					        m_is_conjecture = m_parent->is_conjecture();
 | 
				
			||||||
 | 
					        // m_is_subsume = m_parent->is_subsume();
 | 
				
			||||||
 | 
					        m_gas = m_parent->get_gas();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pob::set_post(expr* post) {
 | 
					void pob::set_post(expr* post) {
 | 
				
			||||||
    app_ref_vector empty_binding(get_ast_manager());
 | 
					    set_post(post, {get_ast_manager()});
 | 
				
			||||||
    set_post(post, empty_binding);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pob::set_post(expr* post, app_ref_vector const &binding) {
 | 
					void pob::set_post(expr* post, app_ref_vector const &binding) {
 | 
				
			||||||
| 
						 | 
					@ -91,6 +98,11 @@ void pob::inherit(pob const &p) {
 | 
				
			||||||
    SASSERT(!is_in_queue());
 | 
					    SASSERT(!is_in_queue());
 | 
				
			||||||
    SASSERT(m_parent == p.m_parent);
 | 
					    SASSERT(m_parent == p.m_parent);
 | 
				
			||||||
    SASSERT(&m_pt == &p.m_pt);
 | 
					    SASSERT(&m_pt == &p.m_pt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- HACK: normalize second time because th_rewriter is not idempotent
 | 
				
			||||||
 | 
					    if (m_post != p.m_post) {
 | 
				
			||||||
 | 
					      normalize(m_post, m_post, false, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    SASSERT(m_post == p.m_post);
 | 
					    SASSERT(m_post == p.m_post);
 | 
				
			||||||
    SASSERT(!m_new_post);
 | 
					    SASSERT(!m_new_post);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,11 +111,21 @@ void pob::inherit(pob const &p) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    m_level = p.m_level;
 | 
					    m_level = p.m_level;
 | 
				
			||||||
    m_depth = p.m_depth;
 | 
					    m_depth = p.m_depth;
 | 
				
			||||||
 | 
					    m_desired_level = std::max(m_desired_level, p.m_desired_level);
 | 
				
			||||||
    m_open = p.m_open;
 | 
					    m_open = p.m_open;
 | 
				
			||||||
    m_use_farkas = p.m_use_farkas;
 | 
					    m_use_farkas = p.m_use_farkas;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_is_conjecture = p.m_is_conjecture;
 | 
				
			||||||
 | 
					    m_enable_local_gen = p.m_enable_local_gen;
 | 
				
			||||||
 | 
					    m_enable_concretize = p.m_enable_concretize;
 | 
				
			||||||
 | 
					    m_is_subsume = p.m_is_subsume;
 | 
				
			||||||
 | 
					    m_enable_expand_bnd_gen = p.m_enable_expand_bnd_gen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    m_weakness = p.m_weakness;
 | 
					    m_weakness = p.m_weakness;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    m_derivation = nullptr;
 | 
					    m_derivation = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_gas = p.m_gas;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pob::close () {
 | 
					void pob::close () {
 | 
				
			||||||
| 
						 | 
					@ -756,6 +778,8 @@ void pred_transformer::collect_statistics(statistics& st) const
 | 
				
			||||||
               m_must_reachable_watch.get_seconds ());
 | 
					               m_must_reachable_watch.get_seconds ());
 | 
				
			||||||
    st.update("time.spacer.ctp", m_ctp_watch.get_seconds());
 | 
					    st.update("time.spacer.ctp", m_ctp_watch.get_seconds());
 | 
				
			||||||
    st.update("time.spacer.mbp", m_mbp_watch.get_seconds());
 | 
					    st.update("time.spacer.mbp", m_mbp_watch.get_seconds());
 | 
				
			||||||
 | 
					    // -- Max cluster size can decrease during run
 | 
				
			||||||
 | 
					    st.update("SPACER max cluster size", m_cluster_db.get_max_cluster_size());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void pred_transformer::reset_statistics()
 | 
					void pred_transformer::reset_statistics()
 | 
				
			||||||
| 
						 | 
					@ -1193,6 +1217,7 @@ expr_ref pred_transformer::get_origin_summary (model &mdl,
 | 
				
			||||||
    for (auto* s : summary) {
 | 
					    for (auto* s : summary) {
 | 
				
			||||||
        if (!is_quantifier(s) && !mdl.is_true(s)) {
 | 
					        if (!is_quantifier(s) && !mdl.is_true(s)) {
 | 
				
			||||||
            TRACE("spacer", tout << "Summary not true in the model: " << mk_pp(s, m) << "\n";);
 | 
					            TRACE("spacer", tout << "Summary not true in the model: " << mk_pp(s, m) << "\n";);
 | 
				
			||||||
 | 
					            return expr_ref(m);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					@ -1258,12 +1283,11 @@ void pred_transformer::get_pred_bg_invs(expr_ref_vector& out) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// \brief Returns true if the obligation is already blocked by current lemmas
 | 
					/// \brief Returns true if the obligation is already blocked by current lemmas
 | 
				
			||||||
bool pred_transformer::is_blocked (pob &n, unsigned &uses_level)
 | 
					bool pred_transformer::is_blocked(pob &n, unsigned &uses_level, model_ref *model) {
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    ensure_level (n.level ());
 | 
					    ensure_level (n.level ());
 | 
				
			||||||
    prop_solver::scoped_level _sl (*m_solver, n.level ());
 | 
					    prop_solver::scoped_level _sl (*m_solver, n.level ());
 | 
				
			||||||
    m_solver->set_core (nullptr);
 | 
					    m_solver->set_core (nullptr);
 | 
				
			||||||
    m_solver->set_model (nullptr);
 | 
					    m_solver->set_model(model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expr_ref_vector post(m), _aux(m);
 | 
					    expr_ref_vector post(m), _aux(m);
 | 
				
			||||||
    post.push_back (n.post ());
 | 
					    post.push_back (n.post ());
 | 
				
			||||||
| 
						 | 
					@ -1335,7 +1359,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core,
 | 
				
			||||||
                                     model_ref* model, unsigned& uses_level,
 | 
					                                     model_ref* model, unsigned& uses_level,
 | 
				
			||||||
                                     bool& is_concrete, datalog::rule const*& r,
 | 
					                                     bool& is_concrete, datalog::rule const*& r,
 | 
				
			||||||
                                     bool_vector& reach_pred_used,
 | 
					                                     bool_vector& reach_pred_used,
 | 
				
			||||||
                                     unsigned& num_reuse_reach)
 | 
					                                     unsigned& num_reuse_reach, bool use_iuc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    TRACE("spacer",
 | 
					    TRACE("spacer",
 | 
				
			||||||
          tout << "is-reachable: " << head()->get_name() << " level: "
 | 
					          tout << "is-reachable: " << head()->get_name() << " level: "
 | 
				
			||||||
| 
						 | 
					@ -1349,7 +1373,8 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // prepare the solver
 | 
					    // prepare the solver
 | 
				
			||||||
    prop_solver::scoped_level _sl(*m_solver, n.level());
 | 
					    prop_solver::scoped_level _sl(*m_solver, n.level());
 | 
				
			||||||
    prop_solver::scoped_subset_core _sc (*m_solver, !n.use_farkas_generalizer ());
 | 
					    prop_solver::scoped_subset_core _sc(
 | 
				
			||||||
 | 
					        *m_solver, !(use_iuc && n.use_farkas_generalizer()));
 | 
				
			||||||
    prop_solver::scoped_weakness _sw(*m_solver, 0,
 | 
					    prop_solver::scoped_weakness _sw(*m_solver, 0,
 | 
				
			||||||
                                     ctx.weak_abs() ? n.weakness() : UINT_MAX);
 | 
					                                     ctx.weak_abs() ? n.weakness() : UINT_MAX);
 | 
				
			||||||
    m_solver->set_core(core);
 | 
					    m_solver->set_core(core);
 | 
				
			||||||
| 
						 | 
					@ -1406,9 +1431,10 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core,
 | 
				
			||||||
            r = find_rule(**model, is_concrete, reach_pred_used, num_reuse_reach);
 | 
					            r = find_rule(**model, is_concrete, reach_pred_used, num_reuse_reach);
 | 
				
			||||||
            TRACE("spacer", 
 | 
					            TRACE("spacer", 
 | 
				
			||||||
                  tout << "reachable is_sat: " << is_sat << " "
 | 
					                  tout << "reachable is_sat: " << is_sat << " "
 | 
				
			||||||
                  << r << " is_concrete " << is_concrete << " rused: " << reach_pred_used << "\n";
 | 
					                  << r << " is_concrete " << is_concrete << " rused: " << reach_pred_used << "\n";);
 | 
				
			||||||
                  ctx.get_datalog_context().get_rule_manager().display_smt2(*r, tout) << "\n";
 | 
					            CTRACE("spacer", r,
 | 
				
			||||||
                  );
 | 
					                   ctx.get_datalog_context().get_rule_manager().display_smt2(*r, tout);
 | 
				
			||||||
 | 
					                   tout << "\n";);
 | 
				
			||||||
            TRACE("spacer_sat", tout << "model is:\n" << **model << "\n";);
 | 
					            TRACE("spacer_sat", tout << "model is:\n" << **model << "\n";);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2272,7 +2298,8 @@ context::context(fp_params const& params, ast_manager& m) :
 | 
				
			||||||
    m_last_result(l_undef),
 | 
					    m_last_result(l_undef),
 | 
				
			||||||
    m_inductive_lvl(0),
 | 
					    m_inductive_lvl(0),
 | 
				
			||||||
    m_expanded_lvl(0),
 | 
					    m_expanded_lvl(0),
 | 
				
			||||||
    m_json_marshaller(this),
 | 
					    m_global_gen(nullptr),
 | 
				
			||||||
 | 
					    m_expand_bnd_gen(nullptr),
 | 
				
			||||||
    m_trace_stream(nullptr) {
 | 
					    m_trace_stream(nullptr) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    params_ref p;
 | 
					    params_ref p;
 | 
				
			||||||
| 
						 | 
					@ -2290,6 +2317,7 @@ context::context(fp_params const& params, ast_manager& m) :
 | 
				
			||||||
    m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts);
 | 
					    m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts);
 | 
				
			||||||
    m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts);
 | 
					    m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_lmma_cluster = alloc(lemma_cluster_finder, m);
 | 
				
			||||||
    updt_params();
 | 
					    updt_params();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (m_params.spacer_trace_file().is_non_empty_string()) {
 | 
					    if (m_params.spacer_trace_file().is_non_empty_string()) {
 | 
				
			||||||
| 
						 | 
					@ -2302,6 +2330,7 @@ context::context(fp_params const& params, ast_manager& m) :
 | 
				
			||||||
context::~context()
 | 
					context::~context()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    reset_lemma_generalizers();
 | 
					    reset_lemma_generalizers();
 | 
				
			||||||
 | 
					    dealloc(m_lmma_cluster);
 | 
				
			||||||
    reset();
 | 
					    reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (m_trace_stream) {
 | 
					    if (m_trace_stream) {
 | 
				
			||||||
| 
						 | 
					@ -2347,7 +2376,13 @@ void context::updt_params() {
 | 
				
			||||||
    m_restart_initial_threshold = m_params.spacer_restart_initial_threshold();
 | 
					    m_restart_initial_threshold = m_params.spacer_restart_initial_threshold();
 | 
				
			||||||
    m_pdr_bfs = m_params.spacer_gpdr_bfs();
 | 
					    m_pdr_bfs = m_params.spacer_gpdr_bfs();
 | 
				
			||||||
    m_use_bg_invs = m_params.spacer_use_bg_invs();
 | 
					    m_use_bg_invs = m_params.spacer_use_bg_invs();
 | 
				
			||||||
 | 
					    m_global = m_params.spacer_global();
 | 
				
			||||||
 | 
					    m_expand_bnd = m_params.spacer_expand_bnd();
 | 
				
			||||||
 | 
					    m_gg_conjecture = m_params.spacer_gg_conjecture();
 | 
				
			||||||
 | 
					    m_gg_subsume = m_params.spacer_gg_subsume();
 | 
				
			||||||
 | 
					    m_gg_concretize = m_params.spacer_gg_concretize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_use_iuc = m_params.spacer_use_iuc();
 | 
				
			||||||
    if (m_use_gpdr) {
 | 
					    if (m_use_gpdr) {
 | 
				
			||||||
        // set options to be compatible with GPDR
 | 
					        // set options to be compatible with GPDR
 | 
				
			||||||
        m_weak_abs = false;
 | 
					        m_weak_abs = false;
 | 
				
			||||||
| 
						 | 
					@ -2665,7 +2700,8 @@ void context::init_lemma_generalizers()
 | 
				
			||||||
    //m_lemma_generalizers.push_back (alloc (unsat_core_generalizer, *this));
 | 
					    //m_lemma_generalizers.push_back (alloc (unsat_core_generalizer, *this));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (m_use_ind_gen) {
 | 
					    if (m_use_ind_gen) {
 | 
				
			||||||
        m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0));
 | 
					        // m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0));
 | 
				
			||||||
 | 
					        m_lemma_generalizers.push_back(alloc_lemma_inductive_generalizer(*this));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // after the lemma is minimized (maybe should also do before)
 | 
					    // after the lemma is minimized (maybe should also do before)
 | 
				
			||||||
| 
						 | 
					@ -2678,6 +2714,15 @@ void context::init_lemma_generalizers()
 | 
				
			||||||
        m_lemma_generalizers.push_back(alloc(lemma_array_eq_generalizer, *this));
 | 
					        m_lemma_generalizers.push_back(alloc(lemma_array_eq_generalizer, *this));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_global) {
 | 
				
			||||||
 | 
					        m_global_gen = alloc(lemma_global_generalizer, *this);
 | 
				
			||||||
 | 
					        m_lemma_generalizers.push_back(m_global_gen);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_expand_bnd) {
 | 
				
			||||||
 | 
					        m_expand_bnd_gen = alloc(lemma_expand_bnd_generalizer, *this);
 | 
				
			||||||
 | 
					        m_lemma_generalizers.push_back(m_expand_bnd_gen);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (m_validate_lemmas) {
 | 
					    if (m_validate_lemmas) {
 | 
				
			||||||
        m_lemma_generalizers.push_back(alloc(lemma_sanity_checker, *this));
 | 
					        m_lemma_generalizers.push_back(alloc(lemma_sanity_checker, *this));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -3020,9 +3065,7 @@ lbool context::solve_core (unsigned from_lvl)
 | 
				
			||||||
        if (check_reachability()) { return l_true; }
 | 
					        if (check_reachability()) { return l_true; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (lvl > 0 && m_use_propagate)
 | 
					        if (lvl > 0 && m_use_propagate)
 | 
				
			||||||
            if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { dump_json(); return l_false; }
 | 
					            if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { return l_false; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        dump_json();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (is_inductive()){
 | 
					        if (is_inductive()){
 | 
				
			||||||
            return l_false;
 | 
					            return l_false;
 | 
				
			||||||
| 
						 | 
					@ -3070,6 +3113,8 @@ void context::log_expand_pob(pob &n) {
 | 
				
			||||||
        if (n.parent()) pob_id = std::to_string(n.parent()->post()->get_id());
 | 
					        if (n.parent()) pob_id = std::to_string(n.parent()->post()->get_id());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        *m_trace_stream << "** expand-pob: " << n.pt().head()->get_name()
 | 
					        *m_trace_stream << "** expand-pob: " << n.pt().head()->get_name()
 | 
				
			||||||
 | 
					                        << (n.is_conjecture() ? " CONJ" : "")
 | 
				
			||||||
 | 
					                        << (n.is_subsume() ? " SUBS" : "")
 | 
				
			||||||
                        << " level: " << n.level()
 | 
					                        << " level: " << n.level()
 | 
				
			||||||
                        << " depth: " << (n.depth() - m_pob_queue.min_depth())
 | 
					                        << " depth: " << (n.depth() - m_pob_queue.min_depth())
 | 
				
			||||||
                        << " exprID: " << n.post()->get_id() << " pobID: " << pob_id << "\n"
 | 
					                        << " exprID: " << n.post()->get_id() << " pobID: " << pob_id << "\n"
 | 
				
			||||||
| 
						 | 
					@ -3077,13 +3122,18 @@ void context::log_expand_pob(pob &n) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    TRACE("spacer", tout << "expand-pob: " << n.pt().head()->get_name()
 | 
					    TRACE("spacer", tout << "expand-pob: " << n.pt().head()->get_name()
 | 
				
			||||||
 | 
					                         << (n.is_conjecture() ? " CONJ" : "")
 | 
				
			||||||
 | 
					                         << (n.is_subsume() ? " SUBS" : "")
 | 
				
			||||||
                         << " level: " << n.level()
 | 
					                         << " level: " << n.level()
 | 
				
			||||||
                         << " depth: " << (n.depth() - m_pob_queue.min_depth())
 | 
					                         << " depth: " << (n.depth() - m_pob_queue.min_depth())
 | 
				
			||||||
                         << " fvsz: " << n.get_free_vars_size() << "\n"
 | 
					                         << " fvsz: " << n.get_free_vars_size()
 | 
				
			||||||
 | 
					                         << " gas: " << n.get_gas() << "\n"
 | 
				
			||||||
                         << mk_pp(n.post(), m) << "\n";);
 | 
					                         << mk_pp(n.post(), m) << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    STRACE("spacer_progress",
 | 
					    STRACE("spacer_progress",
 | 
				
			||||||
           tout << "** expand-pob: " << n.pt().head()->get_name()
 | 
					           tout << "** expand-pob: " << n.pt().head()->get_name()
 | 
				
			||||||
 | 
					                << (n.is_conjecture() ? " CONJ" : "")
 | 
				
			||||||
 | 
					                << (n.is_subsume() ? " SUBS" : "")
 | 
				
			||||||
                << " level: " << n.level()
 | 
					                << " level: " << n.level()
 | 
				
			||||||
                << " depth: " << (n.depth() - m_pob_queue.min_depth()) << "\n"
 | 
					                << " depth: " << (n.depth() - m_pob_queue.min_depth()) << "\n"
 | 
				
			||||||
                << mk_epp(n.post(), m) << "\n\n";);
 | 
					                << mk_epp(n.post(), m) << "\n\n";);
 | 
				
			||||||
| 
						 | 
					@ -3151,13 +3201,21 @@ bool context::check_reachability ()
 | 
				
			||||||
            node = last_reachable;
 | 
					            node = last_reachable;
 | 
				
			||||||
            last_reachable = nullptr;
 | 
					            last_reachable = nullptr;
 | 
				
			||||||
            if (m_pob_queue.is_root(*node)) { return true; }
 | 
					            if (m_pob_queue.is_root(*node)) { return true; }
 | 
				
			||||||
            if (is_reachable (*node->parent())) {
 | 
					
 | 
				
			||||||
                last_reachable = node->parent ();
 | 
					            // do not check the parent if its may pob status is different
 | 
				
			||||||
 | 
					            if (node->parent()->is_may_pob() != node->is_may_pob())
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                last_reachable = nullptr;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (is_reachable(*node->parent())) {
 | 
				
			||||||
 | 
					                last_reachable = node->parent();
 | 
				
			||||||
                SASSERT(last_reachable->is_closed());
 | 
					                SASSERT(last_reachable->is_closed());
 | 
				
			||||||
                last_reachable->close ();
 | 
					                last_reachable->close();
 | 
				
			||||||
            } else if (!node->parent()->is_closed()) {
 | 
					            } else if (!node->parent()->is_closed()) {
 | 
				
			||||||
                /* bump node->parent */
 | 
					                /* bump node->parent */
 | 
				
			||||||
                node->parent ()->bump_weakness();
 | 
					                node->parent()->bump_weakness();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3202,20 +3260,37 @@ bool context::check_reachability ()
 | 
				
			||||||
        case l_true:
 | 
					        case l_true:
 | 
				
			||||||
            SASSERT(m_pob_queue.size() == old_sz);
 | 
					            SASSERT(m_pob_queue.size() == old_sz);
 | 
				
			||||||
            SASSERT(new_pobs.empty());
 | 
					            SASSERT(new_pobs.empty());
 | 
				
			||||||
 | 
					            node->close();
 | 
				
			||||||
            last_reachable = node;
 | 
					            last_reachable = node;
 | 
				
			||||||
            last_reachable->close ();
 | 
					            if (m_pob_queue.is_root(*node)) { return true; }
 | 
				
			||||||
            if (m_pob_queue.is_root(*node)) {return true;}
 | 
					 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case l_false:
 | 
					        case l_false:
 | 
				
			||||||
            SASSERT(m_pob_queue.size() == old_sz);
 | 
					            SASSERT(m_pob_queue.size() == old_sz);
 | 
				
			||||||
 | 
					            // re-queue all pobs introduced by global gen and any pobs that can be blocked at a higher level
 | 
				
			||||||
            for (auto pob : new_pobs) {
 | 
					            for (auto pob : new_pobs) {
 | 
				
			||||||
                if (is_requeue(*pob)) {m_pob_queue.push(*pob);}
 | 
					                TRACE("gg", tout << "pob: is_may_pob " << pob->is_may_pob()
 | 
				
			||||||
 | 
					                      << " with post:\n"
 | 
				
			||||||
 | 
					                      << mk_pp(pob->post(), m) 
 | 
				
			||||||
 | 
					                      << "\n";);
 | 
				
			||||||
 | 
					                //if ((pob->is_may_pob() && pob->post() != node->post()) || is_requeue(*pob)) {
 | 
				
			||||||
 | 
					                if (is_requeue(*pob)) {
 | 
				
			||||||
 | 
					                  TRACE("gg",
 | 
				
			||||||
 | 
					                        tout << "Adding back blocked pob at level "
 | 
				
			||||||
 | 
					                             << pob->level()
 | 
				
			||||||
 | 
					                             << " and depth " << pob->depth() << "\n");
 | 
				
			||||||
 | 
					                    m_pob_queue.push(*pob);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (m_pob_queue.is_root(*node)) {return false;}
 | 
					            if (m_pob_queue.is_root(*node)) {return false;}
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        case l_undef:
 | 
					        case l_undef:
 | 
				
			||||||
            SASSERT(m_pob_queue.size() == old_sz);
 | 
					            SASSERT(m_pob_queue.size() == old_sz);
 | 
				
			||||||
 | 
					            // collapse may pobs if the reachability of one of them cannot
 | 
				
			||||||
 | 
					            // be estimated
 | 
				
			||||||
 | 
					            if ((node->is_may_pob()) && new_pobs.size() == 0) {
 | 
				
			||||||
 | 
					                close_all_may_parents(node);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            for (auto pob : new_pobs) {m_pob_queue.push(*pob);}
 | 
					            for (auto pob : new_pobs) {m_pob_queue.push(*pob);}
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -3228,7 +3303,10 @@ bool context::check_reachability ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// returns true if the given pob can be re-scheduled
 | 
					/// returns true if the given pob can be re-scheduled
 | 
				
			||||||
bool context::is_requeue(pob &n) {
 | 
					bool context::is_requeue(pob &n) {
 | 
				
			||||||
    if (!m_push_pob) {return false;}
 | 
					    // if have not reached desired level, then requeue 
 | 
				
			||||||
 | 
					    if (n.level() <= n.desired_level()) { return true; }
 | 
				
			||||||
 | 
					    if (!m_push_pob) { return false; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unsigned max_depth = m_push_pob_max_depth;
 | 
					    unsigned max_depth = m_push_pob_max_depth;
 | 
				
			||||||
    return (n.level() >= m_pob_queue.max_level() ||
 | 
					    return (n.level() >= m_pob_queue.max_level() ||
 | 
				
			||||||
            m_pob_queue.max_level() - n.level() <= max_depth);
 | 
					            m_pob_queue.max_level() - n.level() <= max_depth);
 | 
				
			||||||
| 
						 | 
					@ -3270,9 +3348,9 @@ bool context::is_reachable(pob &n)
 | 
				
			||||||
    unsigned saved = n.level ();
 | 
					    unsigned saved = n.level ();
 | 
				
			||||||
    // TBD: don't expose private field
 | 
					    // TBD: don't expose private field
 | 
				
			||||||
    n.m_level = infty_level ();
 | 
					    n.m_level = infty_level ();
 | 
				
			||||||
    lbool res = n.pt().is_reachable(n, nullptr, &mdl,
 | 
					    lbool res =
 | 
				
			||||||
                                    uses_level, is_concrete, r,
 | 
					        n.pt().is_reachable(n, nullptr, &mdl, uses_level, is_concrete, r,
 | 
				
			||||||
                                    reach_pred_used, num_reuse_reach);
 | 
					                            reach_pred_used, num_reuse_reach, m_use_iuc);
 | 
				
			||||||
    n.m_level = saved;
 | 
					    n.m_level = saved;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (res != l_true || !is_concrete) {
 | 
					    if (res != l_true || !is_concrete) {
 | 
				
			||||||
| 
						 | 
					@ -3326,16 +3404,6 @@ bool context::is_reachable(pob &n)
 | 
				
			||||||
    return next ? is_reachable(*next) : true;
 | 
					    return next ? is_reachable(*next) : true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void context::dump_json()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    if (m_params.spacer_print_json().is_non_empty_string()) {
 | 
					 | 
				
			||||||
        std::ofstream of;
 | 
					 | 
				
			||||||
        of.open(m_params.spacer_print_json().bare_str());
 | 
					 | 
				
			||||||
        m_json_marshaller.marshal(of);
 | 
					 | 
				
			||||||
        of.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void context::predecessor_eh()
 | 
					void context::predecessor_eh()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    for (unsigned i = 0; i < m_callbacks.size(); i++) {
 | 
					    for (unsigned i = 0; i < m_callbacks.size(); i++) {
 | 
				
			||||||
| 
						 | 
					@ -3446,14 +3514,15 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
    log_expand_pob(n);
 | 
					    log_expand_pob(n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    stopwatch watch;
 | 
					    stopwatch watch;
 | 
				
			||||||
    IF_VERBOSE (1, verbose_stream () << "expand: " << n.pt ().head ()->get_name ()
 | 
					    IF_VERBOSE(1, verbose_stream()
 | 
				
			||||||
                << " (" << n.level () << ", "
 | 
					                      << "expand: " << n.pt().head()->get_name() << " ("
 | 
				
			||||||
 | 
					                      << n.level() << ", "
 | 
				
			||||||
                << (n.depth () - m_pob_queue.min_depth ()) << ") "
 | 
					                << (n.depth () - m_pob_queue.min_depth ()) << ") "
 | 
				
			||||||
                << (n.use_farkas_generalizer () ? "FAR " : "SUB ")
 | 
					                << (n.use_farkas_generalizer () ? "FAR " : "SUB ")
 | 
				
			||||||
                << " w(" << n.weakness() << ") "
 | 
					                      << (n.is_conjecture() ? "CONJ " : "")
 | 
				
			||||||
                << n.post ()->get_id ();
 | 
					                      << (n.is_subsume() ? " SUBS" : "") << " w("
 | 
				
			||||||
                verbose_stream().flush ();
 | 
					                      << n.weakness() << ") " << n.post()->get_id();
 | 
				
			||||||
                watch.start (););
 | 
					               verbose_stream().flush(); watch.start(););
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // used in case n is unreachable
 | 
					    // used in case n is unreachable
 | 
				
			||||||
    unsigned uses_level = infty_level ();
 | 
					    unsigned uses_level = infty_level ();
 | 
				
			||||||
| 
						 | 
					@ -3468,25 +3537,59 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
    unsigned num_reuse_reach = 0;
 | 
					    unsigned num_reuse_reach = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (m_push_pob && n.pt().is_blocked(n, uses_level)) {
 | 
					    if (!n.is_may_pob() && m_push_pob && n.pt().is_blocked(n, uses_level)) {
 | 
				
			||||||
        // if (!m_pob_queue.is_root (n)) n.close ();
 | 
					        // if (!m_pob_queue.is_root (n)) n.close ();
 | 
				
			||||||
        IF_VERBOSE (1, verbose_stream () << " K "
 | 
					        IF_VERBOSE (1, verbose_stream () << " K "
 | 
				
			||||||
                    << std::fixed << std::setprecision(2)
 | 
					                    << std::fixed << std::setprecision(2)
 | 
				
			||||||
                    << watch.get_seconds () << "\n";);
 | 
					                      << watch.get_seconds () << "\n";);
 | 
				
			||||||
        n.inc_level();
 | 
					          n.inc_level();
 | 
				
			||||||
        out.push_back(&n);
 | 
					          out.push_back(&n);
 | 
				
			||||||
        return l_false;
 | 
					          return l_false;
 | 
				
			||||||
    }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (/* XXX noop */ n.pt().is_qblocked(n)) {
 | 
					      if (/* XXX noop */ n.pt().is_qblocked(n)) {
 | 
				
			||||||
        STRACE("spacer_progress",
 | 
					          STRACE("spacer_progress",
 | 
				
			||||||
               tout << "This pob can be blocked by instantiation\n";);
 | 
					                tout << "This pob can be blocked by instantiation\n";);
 | 
				
			||||||
    }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if ((n.is_may_pob()) && n.get_gas() == 0) {
 | 
				
			||||||
 | 
					          TRACE("global", tout << "Cant prove may pob. Collapsing "
 | 
				
			||||||
 | 
					                              << mk_pp(n.post(), m) << "\n";);
 | 
				
			||||||
 | 
					          m_stats.m_num_pob_ofg++;
 | 
				
			||||||
 | 
					          return l_undef;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // Decide whether to concretize pob
 | 
				
			||||||
 | 
					      // get a model that satisfies the pob and the current set of lemmas
 | 
				
			||||||
 | 
					      // TODO: if push_pob is enabled, avoid calling is_blocked twice
 | 
				
			||||||
 | 
					      if (m_gg_concretize && n.is_concretize_enabled() &&
 | 
				
			||||||
 | 
					          !n.pt().is_blocked(n, uses_level, &model)) {
 | 
				
			||||||
 | 
					        TRACE("global",
 | 
				
			||||||
 | 
					              tout << "Concretizing: " << mk_pp(n.post(), m) << "\n"
 | 
				
			||||||
 | 
					              << "\t" << n.get_gas() << " attempts left\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          SASSERT(m_global_gen);
 | 
				
			||||||
 | 
					          if (pob *new_pob = m_global_gen->mk_concretize_pob(n, model)) {
 | 
				
			||||||
 | 
					              m_stats.m_num_concretize++;
 | 
				
			||||||
 | 
					              out.push_back(new_pob);
 | 
				
			||||||
 | 
					              out.push_back(&n);
 | 
				
			||||||
 | 
					              IF_VERBOSE(1, verbose_stream()
 | 
				
			||||||
 | 
					                                << " C " << std::fixed << std::setprecision(2)
 | 
				
			||||||
 | 
					                                << watch.get_seconds() << "\n";);
 | 
				
			||||||
 | 
					              unsigned gas = n.get_gas();
 | 
				
			||||||
 | 
					              SASSERT(gas > 0);
 | 
				
			||||||
 | 
					              // dec gas for orig pob to limit number of concretizations
 | 
				
			||||||
 | 
					              new_pob->set_gas(gas--);
 | 
				
			||||||
 | 
					              n.set_gas(gas);
 | 
				
			||||||
 | 
					              return l_undef;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    model = nullptr;
 | 
				
			||||||
    predecessor_eh();
 | 
					    predecessor_eh();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r,
 | 
					    lbool res =
 | 
				
			||||||
                                      reach_pred_used, num_reuse_reach);
 | 
					        n.pt().is_reachable(n, &cube, &model, uses_level, is_concrete, r,
 | 
				
			||||||
 | 
					                            reach_pred_used, num_reuse_reach, m_use_iuc);
 | 
				
			||||||
    if (model) model->set_model_completion(false);
 | 
					    if (model) model->set_model_completion(false);
 | 
				
			||||||
    if (res == l_undef && model) res = handle_unknown(n, r, *model);
 | 
					    if (res == l_undef && model) res = handle_unknown(n, r, *model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3534,7 +3637,18 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
                    out.push_back (next);
 | 
					                    out.push_back (next);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if(n.is_subsume())
 | 
				
			||||||
 | 
					                m_stats.m_num_subsume_pob_reachable++;
 | 
				
			||||||
 | 
					            if(n.is_conjecture())
 | 
				
			||||||
 | 
					                m_stats.m_num_conj_failed++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CTRACE("global", n.is_conjecture(),
 | 
				
			||||||
 | 
					                   tout << "Failed to block conjecture "
 | 
				
			||||||
 | 
					                   << n.post()->get_id() << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CTRACE("global", n.is_subsume(),
 | 
				
			||||||
 | 
					                   tout << "Failed to block subsume generalization "
 | 
				
			||||||
 | 
					                        << mk_pp(n.post(), m) << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            IF_VERBOSE(1, verbose_stream () << (next ? " X " : " T ")
 | 
					            IF_VERBOSE(1, verbose_stream () << (next ? " X " : " T ")
 | 
				
			||||||
                       << std::fixed << std::setprecision(2)
 | 
					                       << std::fixed << std::setprecision(2)
 | 
				
			||||||
| 
						 | 
					@ -3565,7 +3679,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
        throw unknown_exception();
 | 
					        throw unknown_exception();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case l_false: {
 | 
					    case l_false: {
 | 
				
			||||||
        // n is unreachable, create new summary facts
 | 
					        // n is unreachable, create a new lemma
 | 
				
			||||||
        timeit _timer (is_trace_enabled("spacer_timeit"),
 | 
					        timeit _timer (is_trace_enabled("spacer_timeit"),
 | 
				
			||||||
                       "spacer::expand_pob::false",
 | 
					                       "spacer::expand_pob::false",
 | 
				
			||||||
                       verbose_stream ());
 | 
					                       verbose_stream ());
 | 
				
			||||||
| 
						 | 
					@ -3573,40 +3687,68 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
        // -- only update expanded level when new lemmas are generated at it.
 | 
					        // -- only update expanded level when new lemmas are generated at it.
 | 
				
			||||||
        if (n.level() < m_expanded_lvl) { m_expanded_lvl = n.level(); }
 | 
					        if (n.level() < m_expanded_lvl) { m_expanded_lvl = n.level(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TRACE("spacer", tout << "cube:\n";
 | 
					        TRACE("spacer", tout << "cube:\n" << cube << "\n";);
 | 
				
			||||||
              for (unsigned j = 0; j < cube.size(); ++j)
 | 
					 | 
				
			||||||
                  tout << mk_pp(cube[j].get(), m) << "\n";);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(n.is_conjecture()) m_stats.m_num_conj_success++;
 | 
				
			||||||
 | 
					        if(n.is_subsume()) m_stats.m_num_subsume_pob_blckd++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pob_ref nref(&n);
 | 
					        pob_ref nref(&n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // -- create lemma from a pob and last unsat core
 | 
					        // -- create lemma from a pob and last unsat core
 | 
				
			||||||
        lemma_ref lemma = alloc(class lemma, pob_ref(&n), cube, uses_level);
 | 
					        lemma_ref lemma_pob;
 | 
				
			||||||
 | 
					        if (n.is_local_gen_enabled()) {
 | 
				
			||||||
 | 
					            lemma_pob = alloc(class lemma, nref, cube, uses_level);
 | 
				
			||||||
 | 
					            // -- run all lemma generalizers
 | 
				
			||||||
 | 
					            for (unsigned i = 0;
 | 
				
			||||||
 | 
					                 // -- only generalize if lemma was constructed using farkas
 | 
				
			||||||
 | 
					                 n.use_farkas_generalizer() && !lemma_pob->is_false() &&
 | 
				
			||||||
 | 
					                   i < m_lemma_generalizers.size();
 | 
				
			||||||
 | 
					                 ++i) {
 | 
				
			||||||
 | 
					                checkpoint ();
 | 
				
			||||||
 | 
					                (*m_lemma_generalizers[i])(lemma_pob);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (m_global_gen || m_expand_bnd_gen) {
 | 
				
			||||||
 | 
					            m_stats.m_non_local_gen++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // -- run all lemma generalizers
 | 
					            expr_ref_vector pob_cube(m);
 | 
				
			||||||
        for (unsigned i = 0;
 | 
					            n.get_post_simplified(pob_cube);
 | 
				
			||||||
             // -- only generalize if lemma was constructed using farkas
 | 
					
 | 
				
			||||||
             n.use_farkas_generalizer () && !lemma->is_false() &&
 | 
					            lemma_pob = alloc(class lemma, nref, pob_cube, n.level());
 | 
				
			||||||
                 i < m_lemma_generalizers.size(); ++i) {
 | 
					            TRACE("global", tout << "Disabled local gen on pob (id: "
 | 
				
			||||||
            checkpoint ();
 | 
					                                 << n.post()->get_id() << ")\n"
 | 
				
			||||||
            (*m_lemma_generalizers[i])(lemma);
 | 
					                                 << mk_pp(n.post(), m) << "\n"
 | 
				
			||||||
 | 
					                                 << "Lemma:\n"
 | 
				
			||||||
 | 
					                                 << mk_and(lemma_pob->get_cube()) << "\n";);
 | 
				
			||||||
 | 
					            if (m_global_gen) (*m_global_gen)(lemma_pob);
 | 
				
			||||||
 | 
					            if (m_expand_bnd_gen) (*m_expand_bnd_gen)(lemma_pob);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            lemma_pob = alloc(class lemma, nref, cube, uses_level);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        DEBUG_CODE(
 | 
					 | 
				
			||||||
            lemma_sanity_checker sanity_checker(*this);
 | 
					 | 
				
			||||||
            sanity_checker(lemma);
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CTRACE("global", n.is_conjecture() || n.is_subsume(),
 | 
				
			||||||
 | 
					               tout << "Blocked "
 | 
				
			||||||
 | 
					                    << (n.is_conjecture() ? "conjecture " : "subsume ") << n.post()->get_id()
 | 
				
			||||||
 | 
					                    << " at level " << n.level()
 | 
				
			||||||
 | 
					                    << " using lemma\n" << mk_pp(lemma_pob->get_expr(), m) << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TRACE("spacer", tout << "invariant state: "
 | 
					        DEBUG_CODE(lemma_sanity_checker sanity_checker(*this);
 | 
				
			||||||
              << (is_infty_level(lemma->level())?"(inductive)":"")
 | 
					                   sanity_checker(lemma_pob););
 | 
				
			||||||
              <<  mk_pp(lemma->get_expr(), m) << "\n";);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool v = n.pt().add_lemma (lemma.get());
 | 
					        TRACE("spacer",
 | 
				
			||||||
        if (v) { m_stats.m_num_lemmas++; }
 | 
					              tout << "invariant state: "
 | 
				
			||||||
 | 
					                   << (is_infty_level(lemma_pob->level()) ? "(inductive)" : "")
 | 
				
			||||||
 | 
					                   << mk_pp(lemma_pob->get_expr(), m) << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool is_new = n.pt().add_lemma(lemma_pob.get());
 | 
				
			||||||
 | 
					        if (is_new) {
 | 
				
			||||||
 | 
					            if (m_global) m_lmma_cluster->cluster(lemma_pob);
 | 
				
			||||||
 | 
					            m_stats.m_num_lemmas++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Optionally update the node to be the negation of the lemma
 | 
					        // Optionally update the node to be the negation of the lemma
 | 
				
			||||||
        if (v && m_use_lemma_as_pob) {
 | 
					        if (is_new && m_use_lemma_as_pob) {
 | 
				
			||||||
            expr_ref c(m);
 | 
					            expr_ref c(m);
 | 
				
			||||||
            c = mk_and(lemma->get_cube());
 | 
					            c = mk_and(lemma_pob->get_cube());
 | 
				
			||||||
            // check that the post condition is different
 | 
					            // check that the post condition is different
 | 
				
			||||||
            if (c  != n.post()) {
 | 
					            if (c  != n.post()) {
 | 
				
			||||||
                pob *f = n.pt().find_pob(n.parent(), c);
 | 
					                pob *f = n.pt().find_pob(n.parent(), c);
 | 
				
			||||||
| 
						 | 
					@ -3622,6 +3764,28 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (m_global_gen) {
 | 
				
			||||||
 | 
					            // if global gen is enabled, post-process the pob to create new subsume or conjecture pob
 | 
				
			||||||
 | 
					            if (pob* new_pob = m_global_gen->mk_subsume_pob(n)) {
 | 
				
			||||||
 | 
					                new_pob->set_gas(n.get_gas() - 1);
 | 
				
			||||||
 | 
					                n.set_gas(n.get_gas() - 1);
 | 
				
			||||||
 | 
					                out.push_back(new_pob);
 | 
				
			||||||
 | 
					                m_stats.m_num_subsume_pobs++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TRACE("global_verbose",
 | 
				
			||||||
 | 
					                      tout << "New subsume pob\n" << mk_pp(new_pob->post(), m) << "\n"
 | 
				
			||||||
 | 
					                           << "gas:" << new_pob->get_gas() << "\n";);
 | 
				
			||||||
 | 
					            } else if (pob* new_pob = m_gg_conjecture ? m_global_gen->mk_conjecture_pob(n) : nullptr) {
 | 
				
			||||||
 | 
					                new_pob->set_gas(n.get_gas() - 1);
 | 
				
			||||||
 | 
					                n.set_gas(n.get_gas() - 1);
 | 
				
			||||||
 | 
					                out.push_back(new_pob);
 | 
				
			||||||
 | 
					                m_stats.m_num_conj++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TRACE("global",
 | 
				
			||||||
 | 
					                      tout << "New conjecture pob\n" << mk_pp(new_pob->post(), m) << "\n";);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // schedule the node to be placed back in the queue
 | 
					        // schedule the node to be placed back in the queue
 | 
				
			||||||
        n.inc_level();
 | 
					        n.inc_level();
 | 
				
			||||||
        out.push_back(&n);
 | 
					        out.push_back(&n);
 | 
				
			||||||
| 
						 | 
					@ -3636,7 +3800,24 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out)
 | 
				
			||||||
        return l_false;
 | 
					        return l_false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case l_undef:
 | 
					    case l_undef:
 | 
				
			||||||
        // something went wrong
 | 
					        // if the pob is a may pob, handle specially
 | 
				
			||||||
 | 
					        if (n.is_may_pob()) {
 | 
				
			||||||
 | 
					          // do not create children, but bump weakness
 | 
				
			||||||
 | 
					          // bail out if this does not help
 | 
				
			||||||
 | 
					          // AG: do not know why this is a good strategy
 | 
				
			||||||
 | 
					          if (n.weakness() < 10) {
 | 
				
			||||||
 | 
					              SASSERT(out.empty());
 | 
				
			||||||
 | 
					              n.bump_weakness();
 | 
				
			||||||
 | 
					              return expand_pob(n, out);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            n.close();
 | 
				
			||||||
 | 
					            m_stats.m_expand_pob_undef++;
 | 
				
			||||||
 | 
					            IF_VERBOSE(1, verbose_stream() << " UNDEF "
 | 
				
			||||||
 | 
					                       << std::fixed << std::setprecision(2)
 | 
				
			||||||
 | 
					                       << watch.get_seconds () << "\n";);
 | 
				
			||||||
 | 
					            return l_undef;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (n.weakness() < 10 /* MAX_WEAKENSS */) {
 | 
					        if (n.weakness() < 10 /* MAX_WEAKENSS */) {
 | 
				
			||||||
            bool has_new_child = false;
 | 
					            bool has_new_child = false;
 | 
				
			||||||
            SASSERT(m_weak_abs);
 | 
					            SASSERT(m_weak_abs);
 | 
				
			||||||
| 
						 | 
					@ -3933,6 +4114,11 @@ bool context::create_children(pob& n, datalog::rule const& r,
 | 
				
			||||||
                       !mdl.is_true(n.post())))
 | 
					                       !mdl.is_true(n.post())))
 | 
				
			||||||
    { kid->reset_derivation(); }
 | 
					    { kid->reset_derivation(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (kid->is_may_pob()) {
 | 
				
			||||||
 | 
					        SASSERT(n.get_gas() > 0);
 | 
				
			||||||
 | 
					        n.set_gas(n.get_gas() - 1);
 | 
				
			||||||
 | 
					        kid->set_gas(n.get_gas() - 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    out.push_back(kid);
 | 
					    out.push_back(kid);
 | 
				
			||||||
    m_stats.m_num_queries++;
 | 
					    m_stats.m_num_queries++;
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
| 
						 | 
					@ -3971,6 +4157,17 @@ void context::collect_statistics(statistics& st) const
 | 
				
			||||||
    st.update("SPACER num lemmas", m_stats.m_num_lemmas);
 | 
					    st.update("SPACER num lemmas", m_stats.m_num_lemmas);
 | 
				
			||||||
    // -- number of restarts taken
 | 
					    // -- number of restarts taken
 | 
				
			||||||
    st.update("SPACER restarts", m_stats.m_num_restarts);
 | 
					    st.update("SPACER restarts", m_stats.m_num_restarts);
 | 
				
			||||||
 | 
					    // -- number of time pob abstraction was invoked
 | 
				
			||||||
 | 
					    st.update("SPACER conj", m_stats.m_num_conj);
 | 
				
			||||||
 | 
					    st.update("SPACER conj success", m_stats.m_num_conj_success);
 | 
				
			||||||
 | 
					    st.update("SPACER conj failed",
 | 
				
			||||||
 | 
					              m_stats.m_num_conj_failed);
 | 
				
			||||||
 | 
					    st.update("SPACER pob out of gas", m_stats.m_num_pob_ofg);
 | 
				
			||||||
 | 
					    st.update("SPACER subsume pob", m_stats.m_num_subsume_pobs);
 | 
				
			||||||
 | 
					    st.update("SPACER subsume failed", m_stats.m_num_subsume_pob_reachable);
 | 
				
			||||||
 | 
					    st.update("SPACER subsume success", m_stats.m_num_subsume_pob_blckd);
 | 
				
			||||||
 | 
					    st.update("SPACER concretize", m_stats.m_num_concretize);
 | 
				
			||||||
 | 
					    st.update("SPACER non local gen", m_stats.m_non_local_gen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // -- time to initialize the rules
 | 
					    // -- time to initialize the rules
 | 
				
			||||||
    st.update ("time.spacer.init_rules", m_init_rules_watch.get_seconds ());
 | 
					    st.update ("time.spacer.init_rules", m_init_rules_watch.get_seconds ());
 | 
				
			||||||
| 
						 | 
					@ -3991,6 +4188,7 @@ void context::collect_statistics(statistics& st) const
 | 
				
			||||||
    for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) {
 | 
					    for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) {
 | 
				
			||||||
        m_lemma_generalizers[i]->collect_statistics(st);
 | 
					        m_lemma_generalizers[i]->collect_statistics(st);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    m_lmma_cluster->collect_statistics(st);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void context::reset_statistics()
 | 
					void context::reset_statistics()
 | 
				
			||||||
| 
						 | 
					@ -4008,6 +4206,7 @@ void context::reset_statistics()
 | 
				
			||||||
        m_lemma_generalizers[i]->reset_statistics();
 | 
					        m_lemma_generalizers[i]->reset_statistics();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_lmma_cluster->reset_statistics();
 | 
				
			||||||
    m_init_rules_watch.reset ();
 | 
					    m_init_rules_watch.reset ();
 | 
				
			||||||
    m_solve_watch.reset ();
 | 
					    m_solve_watch.reset ();
 | 
				
			||||||
    m_propagate_watch.reset ();
 | 
					    m_propagate_watch.reset ();
 | 
				
			||||||
| 
						 | 
					@ -4093,8 +4292,6 @@ void context::add_constraint (expr *c, unsigned level)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void context::new_lemma_eh(pred_transformer &pt, lemma *lem) {
 | 
					void context::new_lemma_eh(pred_transformer &pt, lemma *lem) {
 | 
				
			||||||
    if (m_params.spacer_print_json().is_non_empty_string())
 | 
					 | 
				
			||||||
        m_json_marshaller.register_lemma(lem);
 | 
					 | 
				
			||||||
    bool handle=false;
 | 
					    bool handle=false;
 | 
				
			||||||
    for (unsigned i = 0; i < m_callbacks.size(); i++) {
 | 
					    for (unsigned i = 0; i < m_callbacks.size(); i++) {
 | 
				
			||||||
        handle|=m_callbacks[i]->new_lemma();
 | 
					        handle|=m_callbacks[i]->new_lemma();
 | 
				
			||||||
| 
						 | 
					@ -4116,10 +4313,7 @@ void context::new_lemma_eh(pred_transformer &pt, lemma *lem) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void context::new_pob_eh(pob *p) {
 | 
					void context::new_pob_eh(pob *p) { }
 | 
				
			||||||
    if (m_params.spacer_print_json().is_non_empty_string())
 | 
					 | 
				
			||||||
        m_json_marshaller.register_pob(p);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool context::is_inductive() {
 | 
					bool context::is_inductive() {
 | 
				
			||||||
    // check that inductive level (F infinity) of the query predicate
 | 
					    // check that inductive level (F infinity) of the query predicate
 | 
				
			||||||
| 
						 | 
					@ -4140,6 +4334,10 @@ inline bool pob_lt_proc::operator() (const pob *pn1, const pob *pn2) const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (n1.depth() != n2.depth()) { return n1.depth() < n2.depth(); }
 | 
					    if (n1.depth() != n2.depth()) { return n1.depth() < n2.depth(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (n1.is_subsume() != n2.is_subsume()) { return n1.is_subsume(); }
 | 
				
			||||||
 | 
					    if (n1.is_conjecture() != n2.is_conjecture()) { return n1.is_conjecture(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (n1.get_gas() != n2.get_gas()) { return n1.get_gas() > n2.get_gas(); }
 | 
				
			||||||
    // -- a more deterministic order of proof obligations in a queue
 | 
					    // -- a more deterministic order of proof obligations in a queue
 | 
				
			||||||
    // if (!n1.get_context ().get_params ().spacer_nondet_tie_break ())
 | 
					    // if (!n1.get_context ().get_params ().spacer_nondet_tie_break ())
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -4193,5 +4391,27 @@ inline bool pob_lt_proc::operator() (const pob *pn1, const pob *pn2) const
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// set gas of each may parent to 0
 | 
				
			||||||
 | 
					// TODO: close siblings as well. kids of a pob are not stored in the pob
 | 
				
			||||||
 | 
					void context::close_all_may_parents(pob_ref node) {
 | 
				
			||||||
 | 
					    pob_ref_vector to_do;
 | 
				
			||||||
 | 
					    to_do.push_back(node.get());
 | 
				
			||||||
 | 
					    while (to_do.size() != 0) {
 | 
				
			||||||
 | 
					        pob_ref t = to_do.back();
 | 
				
			||||||
 | 
					        t->set_gas(0);
 | 
				
			||||||
 | 
					        if (t->is_may_pob()) {
 | 
				
			||||||
 | 
					            t->close();
 | 
				
			||||||
 | 
					        } else
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        to_do.pop_back();
 | 
				
			||||||
 | 
					        to_do.push_back(t->parent());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// construct a simplified version of the post
 | 
				
			||||||
 | 
					void pob::get_post_simplified(expr_ref_vector &pob_cube) {
 | 
				
			||||||
 | 
					    pob_cube.reset();
 | 
				
			||||||
 | 
					    pob_cube.push_back(m_post);
 | 
				
			||||||
 | 
					    flatten_and(pob_cube);
 | 
				
			||||||
 | 
					    simplify_bounds(pob_cube);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										389
									
								
								src/muz/spacer/spacer_convex_closure.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								src/muz/spacer/spacer_convex_closure.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,389 @@
 | 
				
			||||||
 | 
					/**++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spacer_convex_closure.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Compute convex closure of polyhedra
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Hari Govind
 | 
				
			||||||
 | 
					    Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_convex_closure.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/th_rewriter.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					bool is_int_matrix(const spacer::spacer_matrix &matrix) {
 | 
				
			||||||
 | 
					    rational val;
 | 
				
			||||||
 | 
					    for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) {
 | 
				
			||||||
 | 
					        for (unsigned j = 0, cols = matrix.num_cols(); j < cols; j++)
 | 
				
			||||||
 | 
					            if (!matrix.get(i, j).is_int()) return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool is_sorted(const vector<rational> &data) {
 | 
				
			||||||
 | 
					    for (unsigned i = 0; i < data.size() - 1; i++) {
 | 
				
			||||||
 | 
					        if (!(data[i] >= data[i + 1])) return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check whether all elements of \p data are congruent modulo \p m
 | 
				
			||||||
 | 
					bool is_congruent_mod(const vector<rational> &data, const rational &m) {
 | 
				
			||||||
 | 
					    SASSERT(data.size() > 0);
 | 
				
			||||||
 | 
					    rational p = data[0] % m;
 | 
				
			||||||
 | 
					    for (auto k : data)
 | 
				
			||||||
 | 
					        if (k % m != p) return false;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app *mk_bvadd(bv_util &bv, unsigned num, expr *const *args) {
 | 
				
			||||||
 | 
					    if (num == 0) return nullptr;
 | 
				
			||||||
 | 
					    if (num == 1) return is_app(args[0]) ? to_app(args[0]) : nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (num == 2) { return bv.mk_bv_add(args[0], args[1]); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// XXX no mk_bv_add for n-ary bv_add
 | 
				
			||||||
 | 
					    return bv.get_manager().mk_app(bv.get_fid(), OP_BADD, num, args);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					convex_closure::convex_closure(ast_manager &_m)
 | 
				
			||||||
 | 
					    : m(_m), m_arith(m), m_bv(m), m_bv_sz(0), m_enable_implicit(true), m_dim(0),
 | 
				
			||||||
 | 
					      m_data(0, 0), m_col_vars(m), m_kernel(m_data), m_alphas(m),
 | 
				
			||||||
 | 
					      m_implicit_cc(m), m_explicit_cc(m) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_kernel.set_plugin(mk_simplex_kernel_plugin());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void convex_closure::reset(unsigned n_cols) {
 | 
				
			||||||
 | 
					    m_dim = n_cols;
 | 
				
			||||||
 | 
					    m_kernel.reset();
 | 
				
			||||||
 | 
					    m_data.reset(m_dim);
 | 
				
			||||||
 | 
					    m_col_vars.reset();
 | 
				
			||||||
 | 
					    m_col_vars.reserve(m_dim);
 | 
				
			||||||
 | 
					    m_dead_cols.reset();
 | 
				
			||||||
 | 
					    m_dead_cols.reserve(m_dim, false);
 | 
				
			||||||
 | 
					    m_alphas.reset();
 | 
				
			||||||
 | 
					    m_bv_sz = 0;
 | 
				
			||||||
 | 
					    m_enable_implicit = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void convex_closure::collect_statistics(statistics &st) const {
 | 
				
			||||||
 | 
					    st.update("time.spacer.solve.reach.gen.global.cc",
 | 
				
			||||||
 | 
					              m_st.watch.get_seconds());
 | 
				
			||||||
 | 
					    st.update("SPACER cc num dim reduction success", m_st.m_num_reductions);
 | 
				
			||||||
 | 
					    st.update("SPACER cc max reduced dim", m_st.m_max_dim);
 | 
				
			||||||
 | 
					    m_kernel.collect_statistics(st);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// call m_kernel to reduce dimensions of m_data
 | 
				
			||||||
 | 
					// return the rank of m_data
 | 
				
			||||||
 | 
					unsigned convex_closure::reduce() {
 | 
				
			||||||
 | 
					    if (m_dim <= 1) return m_dim;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool has_kernel = m_kernel.compute_kernel();
 | 
				
			||||||
 | 
					    if (!has_kernel) {
 | 
				
			||||||
 | 
					        TRACE("cvx_dbg",
 | 
				
			||||||
 | 
					              tout << "No linear dependencies between pattern vars\n";);
 | 
				
			||||||
 | 
					        return m_dim;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const spacer_matrix &ker = m_kernel.get_kernel();
 | 
				
			||||||
 | 
					    SASSERT(ker.num_rows() > 0);
 | 
				
			||||||
 | 
					    SASSERT(ker.num_rows() <= m_dim);
 | 
				
			||||||
 | 
					    SASSERT(ker.num_cols() == m_dim + 1);
 | 
				
			||||||
 | 
					    // m_dim - ker.num_rows() is the number of variables that have no linear
 | 
				
			||||||
 | 
					    // dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (auto v : m_kernel.get_basic_vars())
 | 
				
			||||||
 | 
					        // XXX sometimes a constant can be basic, need to find a way to
 | 
				
			||||||
 | 
					        // switch it to var
 | 
				
			||||||
 | 
					        if (v < m_dead_cols.size()) m_dead_cols[v] = true;
 | 
				
			||||||
 | 
					    return m_dim - ker.num_rows();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// For row \p row in m_kernel, construct the equality:
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// row * m_col_vars = 0
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// In the equality, exactly one variable from  m_col_vars is on the lhs
 | 
				
			||||||
 | 
					void convex_closure::kernel_row2eq(const vector<rational> &row, expr_ref &out) {
 | 
				
			||||||
 | 
					    expr_ref_buffer lhs(m);
 | 
				
			||||||
 | 
					    expr_ref e1(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool is_int = false;
 | 
				
			||||||
 | 
					    for (unsigned i = 0, sz = row.size(); i < sz; ++i) {
 | 
				
			||||||
 | 
					        rational val_i = row.get(i);
 | 
				
			||||||
 | 
					        if (val_i.is_zero()) continue;
 | 
				
			||||||
 | 
					        SASSERT(val_i.is_int());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (i < sz - 1) {
 | 
				
			||||||
 | 
					            e1 = m_col_vars.get(i);
 | 
				
			||||||
 | 
					            is_int |= m_arith.is_int(e1);
 | 
				
			||||||
 | 
					            mul_by_rat(e1, val_i);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            e1 = mk_numeral(val_i, is_int);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        lhs.push_back(e1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    e1 = !has_bv() ? mk_add(lhs) : mk_bvadd(m_bv, lhs.size(), lhs.data());
 | 
				
			||||||
 | 
					    e1 = m.mk_eq(e1, mk_numeral(rational::zero(), is_int));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // revisit this simplification step, it is here only to prevent/simplify
 | 
				
			||||||
 | 
					    // formula construction everywhere else
 | 
				
			||||||
 | 
					    params_ref params;
 | 
				
			||||||
 | 
					    params.set_bool("som", true);
 | 
				
			||||||
 | 
					    params.set_bool("flat", true);
 | 
				
			||||||
 | 
					    th_rewriter rw(m, params);
 | 
				
			||||||
 | 
					    rw(e1, out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Generates linear equalities implied by m_data
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// the linear equalities are m_kernel * m_col_vars = 0 (where * is matrix
 | 
				
			||||||
 | 
					/// multiplication) the new equalities are stored in m_col_vars for each row
 | 
				
			||||||
 | 
					/// [0, 1, 0, 1 , 1] in m_kernel, the equality v1 = -1*v3 + -1*1 is
 | 
				
			||||||
 | 
					/// constructed and stored at index 1 of m_col_vars
 | 
				
			||||||
 | 
					void convex_closure::kernel2fmls(expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    // assume kernel has been computed already
 | 
				
			||||||
 | 
					    const spacer_matrix &kern = m_kernel.get_kernel();
 | 
				
			||||||
 | 
					    SASSERT(kern.num_rows() > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("cvx_dbg", kern.display(tout););
 | 
				
			||||||
 | 
					    expr_ref eq(m);
 | 
				
			||||||
 | 
					    for (unsigned i = kern.num_rows(); i > 0; i--) {
 | 
				
			||||||
 | 
					        auto &row = kern.get_row(i - 1);
 | 
				
			||||||
 | 
					        kernel_row2eq(row, eq);
 | 
				
			||||||
 | 
					        out.push_back(eq);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					expr *convex_closure::mk_add(const expr_ref_buffer &vec) {
 | 
				
			||||||
 | 
					    SASSERT(!vec.empty());
 | 
				
			||||||
 | 
					    expr_ref s(m);
 | 
				
			||||||
 | 
					    if (vec.size() == 1) {
 | 
				
			||||||
 | 
					        return vec[0];
 | 
				
			||||||
 | 
					    } else if (vec.size() > 1) {
 | 
				
			||||||
 | 
					        return m_arith.mk_add(vec.size(), vec.data());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UNREACHABLE();
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					expr *convex_closure::mk_numeral(const rational &n, bool is_int) {
 | 
				
			||||||
 | 
					    if (!has_bv())
 | 
				
			||||||
 | 
					        return m_arith.mk_numeral(n, is_int);
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        return m_bv.mk_numeral(n, m_bv_sz);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Construct the equality ((m_alphas . m_data[*][i]) = m_col_vars[i])
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Where . is the dot product,  m_data[*][i] is
 | 
				
			||||||
 | 
					/// the ith column of m_data. Add the result to res_vec.
 | 
				
			||||||
 | 
					void convex_closure::cc_col2eq(unsigned col, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    SASSERT(!has_bv());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_buffer sum(m);
 | 
				
			||||||
 | 
					    for (unsigned row = 0, sz = m_data.num_rows(); row < sz; row++) {
 | 
				
			||||||
 | 
					        expr_ref alpha(m);
 | 
				
			||||||
 | 
					        auto n = m_data.get(row, col);
 | 
				
			||||||
 | 
					        if (n.is_zero()) {
 | 
				
			||||||
 | 
					            ; // noop
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            alpha = m_alphas.get(row);
 | 
				
			||||||
 | 
					            if (!n.is_one()) {
 | 
				
			||||||
 | 
					                alpha = m_arith.mk_mul(
 | 
				
			||||||
 | 
					                    m_arith.mk_numeral(n, false /* is_int */), alpha);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (alpha) sum.push_back(alpha);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    SASSERT(!sum.empty());
 | 
				
			||||||
 | 
					    expr_ref s(m);
 | 
				
			||||||
 | 
					    s = mk_add(sum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref v(m);
 | 
				
			||||||
 | 
					    expr *vi = m_col_vars.get(col);
 | 
				
			||||||
 | 
					    v = m_arith.is_int(vi) ? m_arith.mk_to_real(vi) : vi;
 | 
				
			||||||
 | 
					    out.push_back(m.mk_eq(s, v));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void convex_closure::cc2fmls(expr_ref_vector &out) {
 | 
				
			||||||
 | 
					    sort_ref real_sort(m_arith.mk_real(), m);
 | 
				
			||||||
 | 
					    expr_ref zero(m_arith.mk_real(rational::zero()), m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned row = 0, sz = m_data.num_rows(); row < sz; row++) {
 | 
				
			||||||
 | 
					        if (row >= m_alphas.size()) {
 | 
				
			||||||
 | 
					            m_alphas.push_back(m.mk_fresh_const("a!cc", real_sort));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        SASSERT(row < m_alphas.size());
 | 
				
			||||||
 | 
					        // forall j :: alpha_j >= 0
 | 
				
			||||||
 | 
					        out.push_back(m_arith.mk_ge(m_alphas.get(row), zero));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned k = 0, sz = m_col_vars.size(); k < sz; k++) {
 | 
				
			||||||
 | 
					        if (m_col_vars.get(k) && !m_dead_cols[k]) cc_col2eq(k, out);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //(\Sum j . m_new_vars[j]) = 1
 | 
				
			||||||
 | 
					    out.push_back(m.mk_eq(
 | 
				
			||||||
 | 
					        m_arith.mk_add(m_data.num_rows(),
 | 
				
			||||||
 | 
					                       reinterpret_cast<expr *const *>(m_alphas.data())),
 | 
				
			||||||
 | 
					        m_arith.mk_real(rational::one())));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_DIV_BOUND 101
 | 
				
			||||||
 | 
					// check whether \exists m, d s.t data[i] mod m = d. Returns the largest m and
 | 
				
			||||||
 | 
					// corresponding d
 | 
				
			||||||
 | 
					// TODO: find the largest divisor, not the smallest.
 | 
				
			||||||
 | 
					// TODO: improve efficiency
 | 
				
			||||||
 | 
					bool convex_closure::infer_div_pred(const vector<rational> &data, rational &m,
 | 
				
			||||||
 | 
					                                    rational &d) {
 | 
				
			||||||
 | 
					    TRACE("cvx_dbg_verb", {
 | 
				
			||||||
 | 
					        tout << "computing div constraints for ";
 | 
				
			||||||
 | 
					        for (rational r : data) tout << r << " ";
 | 
				
			||||||
 | 
					        tout << "\n";
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    SASSERT(data.size() > 1);
 | 
				
			||||||
 | 
					    SASSERT(is_sorted(data));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m = rational(2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // special handling for even/odd
 | 
				
			||||||
 | 
					    if (is_congruent_mod(data, m)) {
 | 
				
			||||||
 | 
					      mod(data.back(), m, d);
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // hard cut off to save time
 | 
				
			||||||
 | 
					    rational bnd(MAX_DIV_BOUND);
 | 
				
			||||||
 | 
					    rational big = data.back();
 | 
				
			||||||
 | 
					    // AG: why (m < big)?  Note that 'big' is the smallest element of data
 | 
				
			||||||
 | 
					    for (; m < big && m < bnd; m++) {
 | 
				
			||||||
 | 
					        if (is_congruent_mod(data, m)) break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (m >= big) return false;
 | 
				
			||||||
 | 
					    if (m == bnd) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mod(data[0], m, d);
 | 
				
			||||||
 | 
					    SASSERT(d >= rational::zero());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("cvx_dbg_verb", tout << "div constraint generated. cf " << m
 | 
				
			||||||
 | 
					                               << " and off " << d << "\n";);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool convex_closure::compute() {
 | 
				
			||||||
 | 
					    scoped_watch _w_(m_st.watch);
 | 
				
			||||||
 | 
					    SASSERT(is_int_matrix(m_data));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned rank = reduce();
 | 
				
			||||||
 | 
					    // store dim var before rewrite
 | 
				
			||||||
 | 
					    expr_ref var(m_col_vars.get(0), m);
 | 
				
			||||||
 | 
					    if (rank < dims()) {
 | 
				
			||||||
 | 
					        m_st.m_num_reductions++;
 | 
				
			||||||
 | 
					        kernel2fmls(m_explicit_cc);
 | 
				
			||||||
 | 
					        TRACE("cvx_dbg", tout << "Linear equalities true of the matrix "
 | 
				
			||||||
 | 
					                              << mk_and(m_explicit_cc) << "\n";);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_st.m_max_dim = std::max(m_st.m_max_dim, rank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (rank == 0) {
 | 
				
			||||||
 | 
					        // AG: Is this possible?
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    } else if (rank > 1) {
 | 
				
			||||||
 | 
					        if (m_enable_implicit) {
 | 
				
			||||||
 | 
					            TRACE("subsume", tout << "Computing syntactic convex closure\n";);
 | 
				
			||||||
 | 
					            cc2fmls(m_implicit_cc);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SASSERT(rank == 1);
 | 
				
			||||||
 | 
					    cc_1dim(var, m_explicit_cc);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// construct the formula result_var <= bnd or result_var >= bnd
 | 
				
			||||||
 | 
					expr *convex_closure::mk_le_ge(expr *v, rational n, bool is_le) {
 | 
				
			||||||
 | 
					    if (m_arith.is_int_real(v)) {
 | 
				
			||||||
 | 
					        expr *en = m_arith.mk_numeral(n, m_arith.is_int(v));
 | 
				
			||||||
 | 
					        return is_le ? m_arith.mk_le(v, en) : m_arith.mk_ge(v, en);
 | 
				
			||||||
 | 
					    } else if (m_bv.is_bv(v)) {
 | 
				
			||||||
 | 
					        expr *en = m_bv.mk_numeral(n, m_bv.get_bv_size(v->get_sort()));
 | 
				
			||||||
 | 
					        return is_le ? m_bv.mk_ule(v, en) : m_bv.mk_ule(en, v);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        UNREACHABLE();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void convex_closure::cc_1dim(const expr_ref &var, expr_ref_vector &out) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // XXX assumes that var corresponds to col 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // The convex closure over one dimension is just a bound
 | 
				
			||||||
 | 
					    vector<rational> data;
 | 
				
			||||||
 | 
					    m_data.get_col(0, data);
 | 
				
			||||||
 | 
					    auto gt_proc = [](rational const &x, rational const &y) -> bool {
 | 
				
			||||||
 | 
					        return x > y;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    std::sort(data.begin(), data.end(), gt_proc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- compute LB <= var <= UB
 | 
				
			||||||
 | 
					    expr_ref res(m);
 | 
				
			||||||
 | 
					    res = var;
 | 
				
			||||||
 | 
					    // upper-bound
 | 
				
			||||||
 | 
					    out.push_back(mk_le_ge(res, data[0], true));
 | 
				
			||||||
 | 
					    // lower-bound
 | 
				
			||||||
 | 
					    out.push_back(mk_le_ge(res, data.back(), false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- compute divisibility constraints
 | 
				
			||||||
 | 
					    rational cr, off;
 | 
				
			||||||
 | 
					    // add div constraints for all variables.
 | 
				
			||||||
 | 
					    for (unsigned j = 0; j < m_data.num_cols(); j++) {
 | 
				
			||||||
 | 
					        auto *v = m_col_vars.get(j);
 | 
				
			||||||
 | 
					        if (v && (m_arith.is_int(v) || m_bv.is_bv(v))) {
 | 
				
			||||||
 | 
					            data.reset();
 | 
				
			||||||
 | 
					            m_data.get_col(j, data);
 | 
				
			||||||
 | 
					            std::sort(data.begin(), data.end(), gt_proc);
 | 
				
			||||||
 | 
					            if (infer_div_pred(data, cr, off)) {
 | 
				
			||||||
 | 
					                out.push_back(mk_eq_mod(v, cr, off));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					expr *convex_closure::mk_eq_mod(expr *v, rational d, rational r) {
 | 
				
			||||||
 | 
					    expr *res = nullptr;
 | 
				
			||||||
 | 
					    if (m_arith.is_int(v)) {
 | 
				
			||||||
 | 
					        res = m.mk_eq(m_arith.mk_mod(v, m_arith.mk_int(d)), m_arith.mk_int(r));
 | 
				
			||||||
 | 
					    } else if (m_bv.is_bv(v)) {
 | 
				
			||||||
 | 
					        res = m.mk_eq(m_bv.mk_bv_urem(v, m_bv.mk_numeral(d, m_bv_sz)),
 | 
				
			||||||
 | 
					                      m_bv.mk_numeral(r, m_bv_sz));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        UNREACHABLE();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										187
									
								
								src/muz/spacer/spacer_convex_closure.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/muz/spacer/spacer_convex_closure.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,187 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/**++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    spacer_convex_closure.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Compute convex closure of polyhedra
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Hari Govind
 | 
				
			||||||
 | 
					    Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "ast/ast.h"
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_arith_kernel.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_matrix.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_util.h"
 | 
				
			||||||
 | 
					#include "util/statistics.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Computes a convex closure of a set of points
 | 
				
			||||||
 | 
					class convex_closure {
 | 
				
			||||||
 | 
					    struct stats {
 | 
				
			||||||
 | 
					        unsigned m_num_reductions;
 | 
				
			||||||
 | 
					        unsigned m_max_dim;
 | 
				
			||||||
 | 
					        stopwatch watch;
 | 
				
			||||||
 | 
					        stats() { reset(); }
 | 
				
			||||||
 | 
					        void reset() {
 | 
				
			||||||
 | 
					            m_num_reductions = 0;
 | 
				
			||||||
 | 
					            m_max_dim = 0;
 | 
				
			||||||
 | 
					            watch.reset();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    stats m_st;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					    bv_util m_bv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // size of all bit vectors in m_col_vars
 | 
				
			||||||
 | 
					    unsigned m_bv_sz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Enable computation of implicit syntactic convex closure
 | 
				
			||||||
 | 
					    bool m_enable_implicit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // number of columns in \p m_data
 | 
				
			||||||
 | 
					    unsigned m_dim;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // A vector of rational valued points
 | 
				
			||||||
 | 
					    spacer_matrix m_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Variables naming columns in `m_data`
 | 
				
			||||||
 | 
					    // \p m_col_vars[k] is a var for column \p k
 | 
				
			||||||
 | 
					    expr_ref_vector m_col_vars;
 | 
				
			||||||
 | 
					    vector<bool> m_dead_cols;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Kernel of \p m_data
 | 
				
			||||||
 | 
					    // Set at the end of computation
 | 
				
			||||||
 | 
					    spacer_arith_kernel m_kernel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Free variables introduced by syntactic convex closure
 | 
				
			||||||
 | 
					    // These variables are always of sort Real
 | 
				
			||||||
 | 
					    expr_ref_vector m_alphas;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector m_implicit_cc;
 | 
				
			||||||
 | 
					    expr_ref_vector m_explicit_cc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Reduces dimension of \p m_data and returns its rank
 | 
				
			||||||
 | 
					    unsigned reduce();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Constructs an equality corresponding to a given row in the kernel
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The equality is conceptually corresponds to
 | 
				
			||||||
 | 
					    ///    row * m_col_vars = 0
 | 
				
			||||||
 | 
					    /// where row is a row vector and m_col_vars is a column vector.
 | 
				
			||||||
 | 
					    /// However, the equality is put in a form so that exactly one variable from
 | 
				
			||||||
 | 
					    /// \p m_col_vars is on the LHS
 | 
				
			||||||
 | 
					    void kernel_row2eq(const vector<rational> &row, expr_ref &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Construct all linear equations implied by points in \p m_data
 | 
				
			||||||
 | 
					    /// This is defined by \p m_kernel * m_col_vars = 0
 | 
				
			||||||
 | 
					    void kernel2fmls(expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Compute syntactic convex closure of \p m_data
 | 
				
			||||||
 | 
					    void cc2fmls(expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Construct the equality ((m_alphas . m_data[*][k]) = m_col_vars[k])
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// \p m_data[*][k] is the kth column of m_data
 | 
				
			||||||
 | 
					    /// The equality is added to \p out.
 | 
				
			||||||
 | 
					    void cc_col2eq(unsigned k, expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Compute one dimensional convex closure over \p var
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// \p var is the dimension over  which convex closure is computed
 | 
				
			||||||
 | 
					    /// Result is stored in \p out
 | 
				
			||||||
 | 
					    void cc_1dim(const expr_ref &var, expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Computes div constraint implied by a set of data points
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Finds the largest numbers \p m, \p d such that \p m_data[i] mod m = d
 | 
				
			||||||
 | 
					    /// Returns true if successful
 | 
				
			||||||
 | 
					    bool infer_div_pred(const vector<rational> &data, rational &m, rational &d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Constructs a formula \p var ~ n , where  ~ = is_le ? <= : >=
 | 
				
			||||||
 | 
					    expr *mk_le_ge(expr *var, rational n, bool is_le);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr *mk_add(const expr_ref_buffer &vec);
 | 
				
			||||||
 | 
					    expr *mk_numeral(const rational &n, bool is_int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns equality (v = r mod d)
 | 
				
			||||||
 | 
					    expr *mk_eq_mod(expr *v, rational d, rational r);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool has_bv() { return m_bv_sz > 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    convex_closure(ast_manager &_m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Resets all data points
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// n_cols is the number of dimensions of new expected data points
 | 
				
			||||||
 | 
					    void reset(unsigned n_cols);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Turn support for fixed sized bit-vectors of size \p sz
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Disables syntactic convex closure as a side-effect
 | 
				
			||||||
 | 
					    void set_bv(unsigned sz) {
 | 
				
			||||||
 | 
					        SASSERT(sz > 0);
 | 
				
			||||||
 | 
					        m_bv_sz = sz;
 | 
				
			||||||
 | 
					        m_enable_implicit = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// \brief Name dimension \p i with a variable \p v.
 | 
				
			||||||
 | 
					    void set_col_var(unsigned i, expr *v) {
 | 
				
			||||||
 | 
					        SASSERT(i < dims());
 | 
				
			||||||
 | 
					        SASSERT(m_col_vars.get(i) == nullptr);
 | 
				
			||||||
 | 
					        m_col_vars[i] = v;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// \brief Return number of dimensions of each point
 | 
				
			||||||
 | 
					    unsigned dims() const { return m_dim; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// \brief Add an n-dimensional point to convex closure
 | 
				
			||||||
 | 
					    void add_row(const vector<rational> &point) {
 | 
				
			||||||
 | 
					        SASSERT(point.size() == dims());
 | 
				
			||||||
 | 
					        m_data.add_row(point);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool operator()() { return this->compute(); }
 | 
				
			||||||
 | 
					    bool compute();
 | 
				
			||||||
 | 
					    bool has_implicit() { return !m_implicit_cc.empty(); }
 | 
				
			||||||
 | 
					    bool has_explicit() { return !m_explicit_cc.empty(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the implicit component of convex closure (if available)
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Implicit component contains constants from get_alphas() that are
 | 
				
			||||||
 | 
					    /// implicitly existentially quantified
 | 
				
			||||||
 | 
					    const expr_ref_vector &get_implicit() { return m_implicit_cc; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// \brief Return implicit constants in implicit convex closure
 | 
				
			||||||
 | 
					    const expr_ref_vector &get_alphas() const { return m_alphas; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the explicit component of convex closure (if available)
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// The explicit component is in term of column variables
 | 
				
			||||||
 | 
					    const expr_ref_vector &get_explicit() { return m_explicit_cc; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns constants used to name columns
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Explicit convex closure is in terms of these variables
 | 
				
			||||||
 | 
					    const expr_ref_vector &get_col_vars() { return m_col_vars; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void collect_statistics(statistics &st) const;
 | 
				
			||||||
 | 
					    void reset_statistics() { m_st.reset(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										229
									
								
								src/muz/spacer/spacer_expand_bnd_generalizer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/muz/spacer/spacer_expand_bnd_generalizer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,229 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					  Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_expand_bnd_generalizer.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Strengthen lemmas by changing numeral constants inside arithmetic literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_expand_bnd_generalizer.h"
 | 
				
			||||||
 | 
					#include "ast/for_each_expr.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/expr_safe_replace.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					/// Returns true if \p e is arithmetic comparison
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Returns true if \p e is of the form \p lhs op rhs, where
 | 
				
			||||||
 | 
					/// op in {<=, <, >, >=}, and rhs is a numeric value
 | 
				
			||||||
 | 
					bool is_arith_comp(const expr *e, expr *&lhs, rational &rhs, bool &is_int,
 | 
				
			||||||
 | 
					                   ast_manager &m) {
 | 
				
			||||||
 | 
					    arith_util arith(m);
 | 
				
			||||||
 | 
					    expr *e1, *e2;
 | 
				
			||||||
 | 
					    if (m.is_not(e, e1)) return is_arith_comp(e1, lhs, rhs, is_int, m);
 | 
				
			||||||
 | 
					    if (arith.is_le(e, lhs, e2) || arith.is_lt(e, lhs, e2) ||
 | 
				
			||||||
 | 
					        arith.is_ge(e, lhs, e2) || arith.is_gt(e, lhs, e2))
 | 
				
			||||||
 | 
					        return arith.is_numeral(e2, rhs, is_int);
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool is_arith_comp(const expr *e, expr *&lhs, rational &rhs, ast_manager &m) {
 | 
				
			||||||
 | 
					    bool is_int;
 | 
				
			||||||
 | 
					    return is_arith_comp(e, lhs, rhs, is_int, m);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bool is_arith_comp(const expr *e, rational &rhs, ast_manager &m) {
 | 
				
			||||||
 | 
					    expr *lhs;
 | 
				
			||||||
 | 
					    return is_arith_comp(e, lhs, rhs, m);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/// If \p lit is of the form (x op v), replace v with num
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Supports arithmetic literals where op is <, <=, >, >=, or negation
 | 
				
			||||||
 | 
					bool update_bound(const expr *lit, rational num, expr_ref &res,
 | 
				
			||||||
 | 
					                  bool negate = false) {
 | 
				
			||||||
 | 
					    SASSERT(is_app(lit));
 | 
				
			||||||
 | 
					    ast_manager &m = res.get_manager();
 | 
				
			||||||
 | 
					    expr *e1;
 | 
				
			||||||
 | 
					    if (m.is_not(lit, e1)) { return update_bound(e1, num, res, !negate); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    arith_util arith(m);
 | 
				
			||||||
 | 
					    expr *lhs;
 | 
				
			||||||
 | 
					    rational val;
 | 
				
			||||||
 | 
					    bool is_int;
 | 
				
			||||||
 | 
					    if (!is_arith_comp(lit, lhs, val, is_int, m)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res = m.mk_app(to_app(lit)->get_decl(), lhs, arith.mk_numeral(num, is_int));
 | 
				
			||||||
 | 
					    if (negate) { m.mk_not(res); }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace collect_rationals_ns {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Finds rationals in an expression
 | 
				
			||||||
 | 
					struct proc {
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vector<rational> &m_res;
 | 
				
			||||||
 | 
					    proc(ast_manager &a_m, vector<rational> &res)
 | 
				
			||||||
 | 
					        : m(a_m), m_arith(m), m_res(res) {}
 | 
				
			||||||
 | 
					    void operator()(expr *n) const {}
 | 
				
			||||||
 | 
					    void operator()(app *n) {
 | 
				
			||||||
 | 
					        rational val;
 | 
				
			||||||
 | 
					        if (m_arith.is_numeral(n, val)) m_res.push_back(val);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace collect_rationals_ns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Extract all numerals from an expression
 | 
				
			||||||
 | 
					void collect_rationals(expr *e, vector<rational> &res, ast_manager &m) {
 | 
				
			||||||
 | 
					    collect_rationals_ns::proc proc(m, res);
 | 
				
			||||||
 | 
					    quick_for_each_expr(proc, e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_expand_bnd_generalizer::lemma_expand_bnd_generalizer(context &ctx)
 | 
				
			||||||
 | 
					    : lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m) {
 | 
				
			||||||
 | 
					    // -- collect rationals from initial condition and transition relation
 | 
				
			||||||
 | 
					    for (auto &kv : ctx.get_pred_transformers()) {
 | 
				
			||||||
 | 
					        collect_rationals(kv.m_value->init(), m_values, m);
 | 
				
			||||||
 | 
					        collect_rationals(kv.m_value->transition(), m_values, m);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // remove duplicates
 | 
				
			||||||
 | 
					    std::sort(m_values.begin(), m_values.end());
 | 
				
			||||||
 | 
					    auto last = std::unique(m_values.begin(), m_values.end());
 | 
				
			||||||
 | 
					    for (unsigned i = 0, sz = std::distance(last, m_values.end()); i < sz; ++i)
 | 
				
			||||||
 | 
					        m_values.pop_back();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_expand_bnd_generalizer::operator()(lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    scoped_watch _w_(m_st.watch);
 | 
				
			||||||
 | 
					    if (!lemma->get_pob()->is_expand_bnd_enabled()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector cube(lemma->get_cube());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- temporary stores a core
 | 
				
			||||||
 | 
					    expr_ref_vector core(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref lit(m), new_lit(m);
 | 
				
			||||||
 | 
					    rational bnd;
 | 
				
			||||||
 | 
					    // for every literal
 | 
				
			||||||
 | 
					    for (unsigned i = 0, sz = cube.size(); i < sz; i++) {
 | 
				
			||||||
 | 
					        lit = cube.get(i);
 | 
				
			||||||
 | 
					        if (m.is_true(lit)) continue;
 | 
				
			||||||
 | 
					        if (!is_arith_comp(lit, bnd, m)) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TRACE("expand_bnd", tout << "Attempting to expand " << lit << " inside "
 | 
				
			||||||
 | 
					                                 << cube << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // for every value
 | 
				
			||||||
 | 
					        for (rational n : m_values) {
 | 
				
			||||||
 | 
					            if (!is_interesting(lit, bnd, n)) continue;
 | 
				
			||||||
 | 
					            m_st.atmpts++;
 | 
				
			||||||
 | 
					            TRACE("expand_bnd", tout << "Attempting to expand " << lit
 | 
				
			||||||
 | 
					                                     << " with numeral " << n << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // -- update bound on lit
 | 
				
			||||||
 | 
					            VERIFY(update_bound(lit, n, new_lit));
 | 
				
			||||||
 | 
					            // -- update lit to new_lit for a new candidate lemma
 | 
				
			||||||
 | 
					            cube[i] = new_lit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            core.reset();
 | 
				
			||||||
 | 
					            core.append(cube);
 | 
				
			||||||
 | 
					            // -- check that candidate is inductive
 | 
				
			||||||
 | 
					            if (check_inductive(lemma, core)) {
 | 
				
			||||||
 | 
					                expr_fast_mark1 in_core;
 | 
				
			||||||
 | 
					                for (auto *e : core) in_core.mark(e);
 | 
				
			||||||
 | 
					                for (unsigned i = 0, sz = cube.size(); i < sz; ++i) {
 | 
				
			||||||
 | 
					                    if (!in_core.is_marked(cube.get(i))) cube[i] = m.mk_true();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                // move to next literal if the current has been removed
 | 
				
			||||||
 | 
					                if (!in_core.is_marked(new_lit)) break;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // -- candidate not inductive, restore original lit
 | 
				
			||||||
 | 
					                cube[i] = lit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Currently, we allow for only one round of expand bound per lemma
 | 
				
			||||||
 | 
					    // Mark lemma as already expanded so that it is not generalized in this way
 | 
				
			||||||
 | 
					    // again
 | 
				
			||||||
 | 
					    lemma->get_pob()->disable_expand_bnd_gen();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check whether \p candidate is a possible generalization for \p lemma.
 | 
				
			||||||
 | 
					/// Side-effect: update \p lemma with the new candidate
 | 
				
			||||||
 | 
					bool lemma_expand_bnd_generalizer::check_inductive(lemma_ref &lemma,
 | 
				
			||||||
 | 
					                                                   expr_ref_vector &candidate) {
 | 
				
			||||||
 | 
					    TRACE("expand_bnd_verb",
 | 
				
			||||||
 | 
					          tout << "Attempting to update lemma with " << candidate << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned uses_level = 0;
 | 
				
			||||||
 | 
					    auto &pt = lemma->get_pob()->pt();
 | 
				
			||||||
 | 
					    bool res = pt.check_inductive(lemma->level(), candidate, uses_level,
 | 
				
			||||||
 | 
					                                  lemma->weakness());
 | 
				
			||||||
 | 
					    if (res) {
 | 
				
			||||||
 | 
					        m_st.success++;
 | 
				
			||||||
 | 
					        lemma->update_cube(lemma->get_pob(), candidate);
 | 
				
			||||||
 | 
					        lemma->set_level(uses_level);
 | 
				
			||||||
 | 
					        TRACE("expand_bnd", tout << "expand_bnd succeeded with "
 | 
				
			||||||
 | 
					                                 << mk_and(candidate) << " at level "
 | 
				
			||||||
 | 
					                                 << uses_level << "\n";);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check whether lit ==> lit[val |--> n] (barring special cases). That is,
 | 
				
			||||||
 | 
					/// whether \p lit becomes weaker if \p val is replaced with \p n
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// \p lit has to be of the form t <= v where v is a numeral.
 | 
				
			||||||
 | 
					/// Special cases:
 | 
				
			||||||
 | 
					/// In the trivial case in which \p val == \p n, return false.
 | 
				
			||||||
 | 
					/// if lit is an equality or the negation of an equality, return true.
 | 
				
			||||||
 | 
					bool lemma_expand_bnd_generalizer::is_interesting(const expr *lit, rational val,
 | 
				
			||||||
 | 
					                                                  rational new_val) {
 | 
				
			||||||
 | 
					    SASSERT(lit);
 | 
				
			||||||
 | 
					    // the only case in which negation and non negation agree
 | 
				
			||||||
 | 
					    if (val == new_val) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m.is_eq(lit)) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // negation is the actual negation modulo val == n
 | 
				
			||||||
 | 
					    expr *e1;
 | 
				
			||||||
 | 
					    if (m.is_not(lit, e1)) {
 | 
				
			||||||
 | 
					        return m.is_eq(lit) || !is_interesting(e1, val, new_val);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SASSERT(val != new_val);
 | 
				
			||||||
 | 
					    SASSERT(is_app(lit));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (to_app(lit)->get_family_id() != m_arith.get_family_id()) return false;
 | 
				
			||||||
 | 
					    switch (to_app(lit)->get_decl_kind()) {
 | 
				
			||||||
 | 
					    case OP_LE:
 | 
				
			||||||
 | 
					    case OP_LT:
 | 
				
			||||||
 | 
					        return new_val > val;
 | 
				
			||||||
 | 
					    case OP_GT:
 | 
				
			||||||
 | 
					    case OP_GE:
 | 
				
			||||||
 | 
					        return new_val < val;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_expand_bnd_generalizer::collect_statistics(statistics &st) const {
 | 
				
			||||||
 | 
					    st.update("time.spacer.solve.reach.gen.expand", m_st.watch.get_seconds());
 | 
				
			||||||
 | 
					    st.update("SPACER expand_bnd attmpts", m_st.atmpts);
 | 
				
			||||||
 | 
					    st.update("SPACER expand_bnd success", m_st.success);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/muz/spacer/spacer_expand_bnd_generalizer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/muz/spacer/spacer_expand_bnd_generalizer.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					  Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_expand_bnd_generalizer.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Strengthen lemmas by changing numeral constants inside arithmetic literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class lemma_expand_bnd_generalizer : public lemma_generalizer {
 | 
				
			||||||
 | 
					    struct stats {
 | 
				
			||||||
 | 
					        unsigned atmpts;
 | 
				
			||||||
 | 
					        unsigned success;
 | 
				
			||||||
 | 
					        stopwatch watch;
 | 
				
			||||||
 | 
					        stats() { reset(); }
 | 
				
			||||||
 | 
					        void reset() {
 | 
				
			||||||
 | 
					            watch.reset();
 | 
				
			||||||
 | 
					            atmpts = 0;
 | 
				
			||||||
 | 
					            success = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    stats m_st;
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// A set of numeral values that can be used to expand bound
 | 
				
			||||||
 | 
					    vector<rational> m_values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    lemma_expand_bnd_generalizer(context &ctx);
 | 
				
			||||||
 | 
					    ~lemma_expand_bnd_generalizer() override {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void operator()(lemma_ref &lemma) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void collect_statistics(statistics &st) const override;
 | 
				
			||||||
 | 
					    void reset_statistics() override { m_st.reset(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Check whether lit ==> lit[val |--> n] (barring special cases). That is,
 | 
				
			||||||
 | 
					    /// whether \p lit becomes weaker if \p val is replaced with \p n
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// \p lit has to be of the form t <= v where v is a numeral.
 | 
				
			||||||
 | 
					    /// Special cases:
 | 
				
			||||||
 | 
					    /// In the trivial case in which \p val == \p n, return false.
 | 
				
			||||||
 | 
					    /// if lit is an equality or the negation of an equality, return true.
 | 
				
			||||||
 | 
					    bool is_interesting(const expr *lit, rational val, rational n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// check whether \p conj is a possible generalization for \p lemma.
 | 
				
			||||||
 | 
					    /// update \p lemma if it is.
 | 
				
			||||||
 | 
					    bool check_inductive(lemma_ref &lemma, expr_ref_vector &candiate);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ Revision History:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "ast/arith_decl_plugin.h"
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
#include "muz/spacer/spacer_context.h"
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_expand_bnd_generalizer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace spacer {
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -174,5 +175,10 @@ class limit_num_generalizer : public lemma_generalizer {
 | 
				
			||||||
    void collect_statistics(statistics &st) const override;
 | 
					    void collect_statistics(statistics &st) const override;
 | 
				
			||||||
    void reset_statistics() override { m_st.reset(); }
 | 
					    void reset_statistics() override { m_st.reset(); }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
} // namespace spacer
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_generalizer *
 | 
				
			||||||
 | 
					alloc_lemma_inductive_generalizer(spacer::context &ctx,
 | 
				
			||||||
 | 
					                                  bool only_array_eligible = false,
 | 
				
			||||||
 | 
					                                  bool enable_literal_weakening = true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										796
									
								
								src/muz/spacer/spacer_global_generalizer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										796
									
								
								src/muz/spacer/spacer_global_generalizer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,796 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_global_generalizer.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Global Guidance for Spacer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_global_generalizer.h"
 | 
				
			||||||
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include "ast/for_each_expr.h"
 | 
				
			||||||
 | 
					#include "ast/rewriter/expr_safe_replace.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_cluster.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_concretize.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_manager.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_matrix.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_util.h"
 | 
				
			||||||
 | 
					#include "smt/smt_solver.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace spacer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// LOCAL HELPER FUNCTIONS IN ANONYMOUS NAMESPACE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class to_real_stripper {
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    arith_util m_arith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    to_real_stripper(ast_manager &_m) : m(_m), m_arith(m) {}
 | 
				
			||||||
 | 
					    bool operator()(expr_ref &e, unsigned depth = 8) {
 | 
				
			||||||
 | 
					        rational num;
 | 
				
			||||||
 | 
					        if (m_arith.is_int(e)) return true;
 | 
				
			||||||
 | 
					        if (depth == 0) return false;
 | 
				
			||||||
 | 
					        if (!is_app(e)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (m_arith.is_to_real(e)) {
 | 
				
			||||||
 | 
					            // strip to_real()
 | 
				
			||||||
 | 
					            e = to_app(e)->get_arg(0);
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        } else if (m_arith.is_numeral(e, num)) {
 | 
				
			||||||
 | 
					            // convert number to an integer
 | 
				
			||||||
 | 
					            if (denominator(num).is_one()) {
 | 
				
			||||||
 | 
					                e = m_arith.mk_int(num);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app *e_app = to_app(e);
 | 
				
			||||||
 | 
					        expr_ref_buffer args(m);
 | 
				
			||||||
 | 
					        expr_ref kid(m);
 | 
				
			||||||
 | 
					        bool dirty = false;
 | 
				
			||||||
 | 
					        for (unsigned i = 0, sz = e_app->get_num_args(); i < sz; ++i) {
 | 
				
			||||||
 | 
					            auto *arg = e_app->get_arg(i);
 | 
				
			||||||
 | 
					            kid = arg;
 | 
				
			||||||
 | 
					            if (this->operator()(kid, depth - 1)) {
 | 
				
			||||||
 | 
					                dirty |= (kid.get() != arg);
 | 
				
			||||||
 | 
					                args.push_back(std::move(kid));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (dirty)
 | 
				
			||||||
 | 
					            e = m.mk_app(e_app->get_family_id(), e_app->get_decl_kind(),
 | 
				
			||||||
 | 
					                         args.size(), args.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool operator()(expr_ref_vector &vec, unsigned depth = 8) {
 | 
				
			||||||
 | 
					        bool res = true;
 | 
				
			||||||
 | 
					        expr_ref e(m);
 | 
				
			||||||
 | 
					        for (unsigned i = 0, sz = vec.size(); res && i < sz; ++i) {
 | 
				
			||||||
 | 
					            e = vec.get(i);
 | 
				
			||||||
 | 
					            res = this->operator()(e, depth);
 | 
				
			||||||
 | 
					            if (res) { vec[i] = e; }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check whether \p sub contains a mapping to a bv_numeral.
 | 
				
			||||||
 | 
					// return bv_size of the bv_numeral in the first such mapping.
 | 
				
			||||||
 | 
					bool contains_bv(ast_manager &m, const substitution &sub, unsigned &sz) {
 | 
				
			||||||
 | 
					    bv_util m_bv(m);
 | 
				
			||||||
 | 
					    std::pair<unsigned, unsigned> v;
 | 
				
			||||||
 | 
					    expr_offset r;
 | 
				
			||||||
 | 
					    rational num;
 | 
				
			||||||
 | 
					    for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) {
 | 
				
			||||||
 | 
					        sub.get_binding(j, v, r);
 | 
				
			||||||
 | 
					        if (m_bv.is_numeral(r.get_expr(), num, sz)) return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check whether 1) all expressions in the range of \p sub are bv_numerals 2)
 | 
				
			||||||
 | 
					// all bv_numerals in range are of size sz
 | 
				
			||||||
 | 
					bool all_same_sz(ast_manager &m, const substitution &sub, unsigned sz) {
 | 
				
			||||||
 | 
					    bv_util m_bv(m);
 | 
				
			||||||
 | 
					    std::pair<unsigned, unsigned> v;
 | 
				
			||||||
 | 
					    expr_offset r;
 | 
				
			||||||
 | 
					    rational num;
 | 
				
			||||||
 | 
					    unsigned n_sz;
 | 
				
			||||||
 | 
					    for (unsigned j = 0; j < sub.get_num_bindings(); j++) {
 | 
				
			||||||
 | 
					        sub.get_binding(j, v, r);
 | 
				
			||||||
 | 
					        if (!m_bv.is_numeral(r.get_expr(), num, n_sz) || n_sz != sz)
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					lemma_global_generalizer::subsumer::subsumer(ast_manager &a_m, bool ground_pob)
 | 
				
			||||||
 | 
					    : m(a_m), m_arith(m), m_bv(m), m_tags(m), m_used_tags(0), m_col_names(m),
 | 
				
			||||||
 | 
					      m_ground_pob(ground_pob) {
 | 
				
			||||||
 | 
					    scoped_ptr<solver_factory> factory(
 | 
				
			||||||
 | 
					        mk_smt_strategic_solver_factory(symbol::null));
 | 
				
			||||||
 | 
					    m_solver = (*factory)(m, params_ref::get_empty(), false, true, false,
 | 
				
			||||||
 | 
					                          symbol::null);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app *lemma_global_generalizer::subsumer::mk_fresh_tag() {
 | 
				
			||||||
 | 
					    if (m_used_tags == m_tags.size()) {
 | 
				
			||||||
 | 
					        auto *bool_sort = m.mk_bool_sort();
 | 
				
			||||||
 | 
					        // -- create 4 new tags
 | 
				
			||||||
 | 
					        m_tags.push_back(m.mk_fresh_const(symbol("t"), bool_sort));
 | 
				
			||||||
 | 
					        m_tags.push_back(m.mk_fresh_const(symbol("t"), bool_sort));
 | 
				
			||||||
 | 
					        m_tags.push_back(m.mk_fresh_const(symbol("t"), bool_sort));
 | 
				
			||||||
 | 
					        m_tags.push_back(m.mk_fresh_const(symbol("t"), bool_sort));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return m_tags.get(m_used_tags++);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lemma_global_generalizer::lemma_global_generalizer(context &ctx)
 | 
				
			||||||
 | 
					    : lemma_generalizer(ctx), m(ctx.get_ast_manager()),
 | 
				
			||||||
 | 
					      m_subsumer(m, ctx.use_ground_pob()), m_do_subsume(ctx.do_subsume()) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_global_generalizer::operator()(lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    scoped_watch _w_(m_st.watch);
 | 
				
			||||||
 | 
					    generalize(lemma);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_global_generalizer::subsumer::mk_col_names(const lemma_cluster &lc) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_offset r;
 | 
				
			||||||
 | 
					    std::pair<unsigned, unsigned> v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto &lemmas = lc.get_lemmas();
 | 
				
			||||||
 | 
					    SASSERT(!lemmas.empty());
 | 
				
			||||||
 | 
					    const substitution &sub = lemmas.get(0).get_sub();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_col_names.reserve(sub.get_num_bindings());
 | 
				
			||||||
 | 
					    for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) {
 | 
				
			||||||
 | 
					        // get var id (sub is in reverse order)
 | 
				
			||||||
 | 
					        sub.get_binding(sz - 1 - j, v, r);
 | 
				
			||||||
 | 
					        auto *sort = r.get_expr()->get_sort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!m_col_names.get(j) || m_col_names.get(j)->get_sort() != sort) {
 | 
				
			||||||
 | 
					            // create a fresh skolem constant for the jth variable
 | 
				
			||||||
 | 
					            // reuse variables if they are already here and have matching sort
 | 
				
			||||||
 | 
					            m_col_names[j] = m.mk_fresh_const("mrg_cvx!!", sort);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- lcm corresponds to a column, reset them since names have potentially
 | 
				
			||||||
 | 
					    // changed
 | 
				
			||||||
 | 
					    // -- this is a just-in-case
 | 
				
			||||||
 | 
					    m_col_lcm.reset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Populate m_cvx_cls by 1) collecting all substitutions in the cluster \p lc
 | 
				
			||||||
 | 
					// 2) normalizing them to integer numerals
 | 
				
			||||||
 | 
					void lemma_global_generalizer::subsumer::setup_cvx_closure(
 | 
				
			||||||
 | 
					    convex_closure &cc, const lemma_cluster &lc) {
 | 
				
			||||||
 | 
					    expr_offset r;
 | 
				
			||||||
 | 
					    std::pair<unsigned, unsigned> v;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mk_col_names(lc);
 | 
				
			||||||
 | 
					    const lemma_info_vector &lemmas = lc.get_lemmas();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m_col_lcm.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned n_vars = 0;
 | 
				
			||||||
 | 
					    rational num;
 | 
				
			||||||
 | 
					    bool is_first = true;
 | 
				
			||||||
 | 
					    for (const auto &lemma : lemmas) {
 | 
				
			||||||
 | 
					        const substitution &sub = lemma.get_sub();
 | 
				
			||||||
 | 
					        if (is_first) {
 | 
				
			||||||
 | 
					            n_vars = sub.get_num_bindings();
 | 
				
			||||||
 | 
					            m_col_lcm.reserve(n_vars, rational::one());
 | 
				
			||||||
 | 
					            is_first = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned j = 0; j < n_vars; j++) {
 | 
				
			||||||
 | 
					            sub.get_binding(n_vars - 1 - j, v, r);
 | 
				
			||||||
 | 
					            if (is_numeral(r.get_expr(), num)) {
 | 
				
			||||||
 | 
					                m_col_lcm[j] = lcm(m_col_lcm.get(j), abs(denominator(num)));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cc.reset(n_vars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    unsigned bv_width;
 | 
				
			||||||
 | 
					    if (contains_bv(m, lc.get_lemmas()[0].get_sub(), bv_width)) {
 | 
				
			||||||
 | 
					        cc.set_bv(bv_width);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned j = 0; j < n_vars; ++j)
 | 
				
			||||||
 | 
					        cc.set_col_var(j, mk_rat_mul(m_col_lcm.get(j), m_col_names.get(j)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vector<rational> row;
 | 
				
			||||||
 | 
					    for (const auto &lemma : lemmas) {
 | 
				
			||||||
 | 
					        row.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const substitution &sub = lemma.get_sub();
 | 
				
			||||||
 | 
					        for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) {
 | 
				
			||||||
 | 
					            sub.get_binding(sz - 1 - j, v, r);
 | 
				
			||||||
 | 
					            VERIFY(is_numeral(r.get_expr(), num));
 | 
				
			||||||
 | 
					            row.push_back(m_col_lcm.get(j) * num);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- add normalized row to convex closure
 | 
				
			||||||
 | 
					        cc.add_row(row);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Find a representative for \p c
 | 
				
			||||||
 | 
					// TODO: replace with a symbolic representative
 | 
				
			||||||
 | 
					expr *lemma_global_generalizer::subsumer::find_repr(const model_ref &mdl,
 | 
				
			||||||
 | 
					                                                    const app *c) {
 | 
				
			||||||
 | 
					    return mdl->get_const_interp(c->get_decl());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Skolemize implicitly existentially quantified constants
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Constants in \p m_dim_frsh_cnsts are existentially quantified in \p f. They
 | 
				
			||||||
 | 
					/// are replaced by specific skolem constants. The \p out vector is populated
 | 
				
			||||||
 | 
					/// with corresponding instantiations. Currently, instantiations are values
 | 
				
			||||||
 | 
					/// chosen from the model
 | 
				
			||||||
 | 
					void lemma_global_generalizer::subsumer::skolemize_for_quic3(
 | 
				
			||||||
 | 
					    expr_ref &f, const model_ref &mdl, app_ref_vector &out) {
 | 
				
			||||||
 | 
					    unsigned idx = out.size();
 | 
				
			||||||
 | 
					    app_ref sk(m);
 | 
				
			||||||
 | 
					    expr_ref eval(m);
 | 
				
			||||||
 | 
					    expr_safe_replace sub(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector f_cnsts(m);
 | 
				
			||||||
 | 
					    spacer::collect_uninterp_consts(f, f_cnsts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_fast_mark2 marks;
 | 
				
			||||||
 | 
					    for (auto *c : f_cnsts) { marks.mark(c); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned i = 0, sz = m_col_names.size(); i < sz; i++) {
 | 
				
			||||||
 | 
					        app *c = m_col_names.get(i);
 | 
				
			||||||
 | 
					        if (!marks.is_marked(c)) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SASSERT(m_arith.is_int(c));
 | 
				
			||||||
 | 
					        // Make skolem constants for ground pob
 | 
				
			||||||
 | 
					        sk = mk_zk_const(m, i + idx, c->get_sort());
 | 
				
			||||||
 | 
					        eval = find_repr(mdl, c);
 | 
				
			||||||
 | 
					        SASSERT(is_app(eval));
 | 
				
			||||||
 | 
					        out.push_back(to_app(eval));
 | 
				
			||||||
 | 
					        sub.insert(c, sk);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    sub(f.get(), f);
 | 
				
			||||||
 | 
					    TRACE("subsume", tout << "skolemized into " << f << "\n";);
 | 
				
			||||||
 | 
					    m_col_names.reset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool lemma_global_generalizer::subsumer::find_model(
 | 
				
			||||||
 | 
					    const expr_ref_vector &cc, const expr_ref_vector &alphas, expr *bg,
 | 
				
			||||||
 | 
					    model_ref &out_model) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // push because we re-use the solver
 | 
				
			||||||
 | 
					    solver::scoped_push _sp(*m_solver);
 | 
				
			||||||
 | 
					    if (bg) m_solver->assert_expr(bg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- assert syntactic convex closure constraints
 | 
				
			||||||
 | 
					    m_solver->assert_expr(cc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // if there are alphas, we have syntactic convex closure
 | 
				
			||||||
 | 
					    if (!alphas.empty()) {
 | 
				
			||||||
 | 
					        SASSERT(alphas.size() >= 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // try to get an interior point in convex closure that also satisfies bg
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // push because this might be unsat
 | 
				
			||||||
 | 
					            solver::scoped_push _sp2(*m_solver);
 | 
				
			||||||
 | 
					            expr_ref zero(m_arith.mk_real(0), m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (auto *alpha : alphas) {
 | 
				
			||||||
 | 
					                m_solver->assert_expr(m_arith.mk_gt(alpha, zero));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto res = m_solver->check_sat();
 | 
				
			||||||
 | 
					            if (res == l_true) {
 | 
				
			||||||
 | 
					                m_solver->get_model(out_model);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // failed, try to get any point in convex closure
 | 
				
			||||||
 | 
					    auto res = m_solver->check_sat();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (res == l_true) {
 | 
				
			||||||
 | 
					        m_solver->get_model(out_model);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UNREACHABLE();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // something went wrong and there is no model, even though one was expected
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns false if subsumption is not supported for \p lc
 | 
				
			||||||
 | 
					bool lemma_global_generalizer::subsumer::is_handled(const lemma_cluster &lc) {
 | 
				
			||||||
 | 
					    // check whether all substitutions are to bv_numerals
 | 
				
			||||||
 | 
					    unsigned sz = 0;
 | 
				
			||||||
 | 
					    bool bv_clus = contains_bv(m, lc.get_lemmas()[0].get_sub(), sz);
 | 
				
			||||||
 | 
					    // If there are no BV numerals, cases are handled.
 | 
				
			||||||
 | 
					    // TODO: put restriction on Arrays, non linear arithmetic etc
 | 
				
			||||||
 | 
					    if (!bv_clus) return true;
 | 
				
			||||||
 | 
					    if (!all_same_sz(m, lc.get_lemmas()[0].get_sub(), sz)) {
 | 
				
			||||||
 | 
					        TRACE("subsume",
 | 
				
			||||||
 | 
					              tout << "cannot compute cvx cls of different size variables\n";);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_global_generalizer::subsumer::reset() {
 | 
				
			||||||
 | 
					    m_used_tags = 0;
 | 
				
			||||||
 | 
					    m_col_lcm.reset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool lemma_global_generalizer::subsumer::subsume(const lemma_cluster &lc,
 | 
				
			||||||
 | 
					                                                 expr_ref_vector &new_post,
 | 
				
			||||||
 | 
					                                                 app_ref_vector &bindings) {
 | 
				
			||||||
 | 
					    if (!is_handled(lc)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    convex_closure cvx_closure(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    reset();
 | 
				
			||||||
 | 
					    setup_cvx_closure(cvx_closure, lc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // compute convex closure
 | 
				
			||||||
 | 
					    if (!cvx_closure.compute()) { return false; }
 | 
				
			||||||
 | 
					    bool is_syntactic = cvx_closure.has_implicit();
 | 
				
			||||||
 | 
					    if (is_syntactic) { m_st.m_num_syn_cls++; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CTRACE("subsume_verb", is_syntactic,
 | 
				
			||||||
 | 
					           tout << "Convex closure introduced new variables. Implicit part of "
 | 
				
			||||||
 | 
					                   "closure is: "
 | 
				
			||||||
 | 
					                << mk_and(cvx_closure.get_implicit()) << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref grounded(m);
 | 
				
			||||||
 | 
					    ground_free_vars(lc.get_pattern(), grounded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector vec(m);
 | 
				
			||||||
 | 
					    auto &implicit_cc = cvx_closure.get_implicit();
 | 
				
			||||||
 | 
					    auto &explicit_cc = cvx_closure.get_explicit();
 | 
				
			||||||
 | 
					    vec.append(implicit_cc.size(), implicit_cc.data());
 | 
				
			||||||
 | 
					    vec.append(explicit_cc.size(), explicit_cc.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // get a model for mbp
 | 
				
			||||||
 | 
					    model_ref mdl;
 | 
				
			||||||
 | 
					    auto &alphas = cvx_closure.get_alphas();
 | 
				
			||||||
 | 
					    find_model(vec, alphas, grounded, mdl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    app_ref_vector vars(m);
 | 
				
			||||||
 | 
					    expr_ref conj(m);
 | 
				
			||||||
 | 
					    vec.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // eliminate real-valued alphas from syntactic convex closure
 | 
				
			||||||
 | 
					    if (!implicit_cc.empty()) {
 | 
				
			||||||
 | 
					        vec.append(implicit_cc.size(), implicit_cc.data());
 | 
				
			||||||
 | 
					        conj = mk_and(vec);
 | 
				
			||||||
 | 
					        vars.append(alphas.size(),
 | 
				
			||||||
 | 
					                    reinterpret_cast<app *const *>(alphas.data()));
 | 
				
			||||||
 | 
					        qe_project(m, vars, conj, *mdl.get(), true, true, !m_ground_pob);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // mbp failed, not expected, bail out
 | 
				
			||||||
 | 
					        if (!vars.empty()) return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // vec = [implicit_cc]
 | 
				
			||||||
 | 
					    // store full cc, this is what we want to over-approximate explicitly
 | 
				
			||||||
 | 
					    vec.append(explicit_cc.size(), explicit_cc.data());
 | 
				
			||||||
 | 
					    flatten_and(grounded, vec);
 | 
				
			||||||
 | 
					    // vec = [implicit_cc(alpha_j, v_i), explicit_cc(v_i), phi(v_i)]
 | 
				
			||||||
 | 
					    expr_ref full_cc(mk_and(vec), m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vec.reset();
 | 
				
			||||||
 | 
					    if (conj) {
 | 
				
			||||||
 | 
					        // if explicit version of implicit cc was successfully computed
 | 
				
			||||||
 | 
					        // conj is it, but need to ensure it has no to_real()
 | 
				
			||||||
 | 
					        to_real_stripper stripper(m);
 | 
				
			||||||
 | 
					        flatten_and(conj, vec);
 | 
				
			||||||
 | 
					        stripper(vec);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    vec.append(explicit_cc.size(), explicit_cc.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    flatten_and(grounded, vec);
 | 
				
			||||||
 | 
					    // here vec is [cc(v_i), phi(v_i)], and we need to eliminate v_i from it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vars.reset();
 | 
				
			||||||
 | 
					    vars.append(m_col_names.size(),
 | 
				
			||||||
 | 
					                reinterpret_cast<app *const *>(m_col_names.data()));
 | 
				
			||||||
 | 
					    conj = mk_and(vec);
 | 
				
			||||||
 | 
					    qe_project(m, vars, conj, *mdl.get(), true, true, !m_ground_pob);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // failed
 | 
				
			||||||
 | 
					    if (!vars.empty()) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // at the end, new_post must over-approximate the implicit convex closure
 | 
				
			||||||
 | 
					    flatten_and(conj, new_post);
 | 
				
			||||||
 | 
					    return over_approximate(new_post, full_cc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Find a weakening of \p a such that \p b ==> a
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Returns true on success and sets \p a to the result
 | 
				
			||||||
 | 
					bool lemma_global_generalizer::subsumer::over_approximate(expr_ref_vector &a,
 | 
				
			||||||
 | 
					                                                          const expr_ref b) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // B && !(A1 && A2 && A3) is encoded as
 | 
				
			||||||
 | 
					    // B && ((tag1 && !A1) || (tag2 && !A2) || (tag3 && !A3))
 | 
				
			||||||
 | 
					    // iterate and check tags
 | 
				
			||||||
 | 
					    expr_ref_vector tags(m), tagged_a(m);
 | 
				
			||||||
 | 
					    std::string tag_prefix = "o";
 | 
				
			||||||
 | 
					    for (auto *lit : a) {
 | 
				
			||||||
 | 
					        tags.push_back(mk_fresh_tag());
 | 
				
			||||||
 | 
					        tagged_a.push_back(m.mk_implies(tags.back(), lit));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("subsume_verb", tout << "weakening " << mk_and(a)
 | 
				
			||||||
 | 
					                               << " to over approximate " << b << "\n";);
 | 
				
			||||||
 | 
					    solver::scoped_push _sp(*m_solver);
 | 
				
			||||||
 | 
					    m_solver->assert_expr(b);
 | 
				
			||||||
 | 
					    m_solver->assert_expr(push_not(mk_and(tagged_a)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        lbool res = m_solver->check_sat(tags.size(), tags.data());
 | 
				
			||||||
 | 
					        if (res == l_false) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } else if (res == l_undef) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // flip tags for all satisfied literals of !A
 | 
				
			||||||
 | 
					        model_ref mdl;
 | 
				
			||||||
 | 
					        m_solver->get_model(mdl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (unsigned i = 0, sz = a.size(); i < sz; ++i) {
 | 
				
			||||||
 | 
					            if (!m.is_not(tags.get(i)) && mdl->is_false(a.get(i))) {
 | 
				
			||||||
 | 
					                tags[i] = m.mk_not(tags.get(i));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_buffer res(m);
 | 
				
			||||||
 | 
					    // remove all expressions whose tags are false
 | 
				
			||||||
 | 
					    for (unsigned i = 0, sz = tags.size(); i < sz; i++) {
 | 
				
			||||||
 | 
					        if (!m.is_not(tags.get(i))) { res.push_back(a.get(i)); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    a.reset();
 | 
				
			||||||
 | 
					    a.append(res.size(), res.data());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (a.empty()) {
 | 
				
			||||||
 | 
					        // could not find an over approximation
 | 
				
			||||||
 | 
					        TRACE("subsume",
 | 
				
			||||||
 | 
					              tout << "mbp did not over-approximate convex closure\n";);
 | 
				
			||||||
 | 
					        m_st.m_num_no_ovr_approx++;
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("subsume",
 | 
				
			||||||
 | 
					          tout << "over approximate produced " << mk_and(a) << "\n";);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Attempt to set a conjecture on pob \p n.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Done by dropping literal \p lit from
 | 
				
			||||||
 | 
					/// post of \p n. \p lvl is level for conjecture pob. \p gas is the gas for
 | 
				
			||||||
 | 
					/// the conjecture pob returns true if conjecture is set
 | 
				
			||||||
 | 
					bool lemma_global_generalizer::do_conjecture(pob_ref &n, lemma_ref &lemma,
 | 
				
			||||||
 | 
					                                             const expr_ref &lit, unsigned lvl,
 | 
				
			||||||
 | 
					                                             unsigned gas) {
 | 
				
			||||||
 | 
					    arith_util arith(m);
 | 
				
			||||||
 | 
					    expr_ref_vector fml_vec(m);
 | 
				
			||||||
 | 
					    expr_ref n_post(n->post(), m);
 | 
				
			||||||
 | 
					    normalize(n_post, n_post, false, false);
 | 
				
			||||||
 | 
					    // normalize_order(n_post, n_post);
 | 
				
			||||||
 | 
					    fml_vec.push_back(n_post);
 | 
				
			||||||
 | 
					    flatten_and(fml_vec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector conj(m);
 | 
				
			||||||
 | 
					    bool is_filtered = filter_out_lit(fml_vec, lit, conj);
 | 
				
			||||||
 | 
					    expr *e1 = nullptr, *e2 = nullptr;
 | 
				
			||||||
 | 
					    if (!is_filtered &&
 | 
				
			||||||
 | 
					        (arith.is_le(lit, e1, e2) || arith.is_ge(lit, e1, e2))) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if lit is '<=' or '>=', try matching '=='
 | 
				
			||||||
 | 
					        is_filtered =
 | 
				
			||||||
 | 
					            filter_out_lit(fml_vec, expr_ref(m.mk_eq(e1, e2), m), conj);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!is_filtered) {
 | 
				
			||||||
 | 
					        // -- try using the corresponding lemma instead
 | 
				
			||||||
 | 
					        conj.reset();
 | 
				
			||||||
 | 
					        n_post = mk_and(lemma->get_cube());
 | 
				
			||||||
 | 
					        normalize_order(n_post, n_post);
 | 
				
			||||||
 | 
					        fml_vec.reset();
 | 
				
			||||||
 | 
					        fml_vec.push_back(n_post);
 | 
				
			||||||
 | 
					        flatten_and(fml_vec);
 | 
				
			||||||
 | 
					        is_filtered = filter_out_lit(fml_vec, lit, conj);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SASSERT(0 < gas && gas < UINT_MAX);
 | 
				
			||||||
 | 
					    if (conj.empty()) {
 | 
				
			||||||
 | 
					        // If the pob cannot be abstracted, stop using generalization on
 | 
				
			||||||
 | 
					        // it
 | 
				
			||||||
 | 
					        TRACE("global", tout << "stop local generalization on pob " << n_post
 | 
				
			||||||
 | 
					                             << " id is " << n_post->get_id() << "\n";);
 | 
				
			||||||
 | 
					        n->disable_local_gen();
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    } else if (!is_filtered) {
 | 
				
			||||||
 | 
					        // The literal to be abstracted is not in the pob
 | 
				
			||||||
 | 
					        TRACE("global", tout << "Conjecture failed:\n"
 | 
				
			||||||
 | 
					                             << lit << "\n"
 | 
				
			||||||
 | 
					                             << n_post << "\n"
 | 
				
			||||||
 | 
					                             << "conj:" << conj << "\n";);
 | 
				
			||||||
 | 
					        n->disable_local_gen();
 | 
				
			||||||
 | 
					        m_st.m_num_cant_abs++;
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pob *root = n->parent();
 | 
				
			||||||
 | 
					    while (root->parent()) root = root->parent();
 | 
				
			||||||
 | 
					    scoped_ptr<pob> new_pob = alloc(pob, root, n->pt(), lvl, n->depth(), false);
 | 
				
			||||||
 | 
					    if (!new_pob) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new_pob->set_desired_level(n->level());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new_pob->set_post(mk_and(conj));
 | 
				
			||||||
 | 
					    new_pob->set_conjecture();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- register with current pob
 | 
				
			||||||
 | 
					    n->set_data(new_pob.detach());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- update properties of the current pob itself
 | 
				
			||||||
 | 
					    n->set_expand_bnd();
 | 
				
			||||||
 | 
					    n->set_gas(gas);
 | 
				
			||||||
 | 
					    n->disable_local_gen();
 | 
				
			||||||
 | 
					    TRACE("global", tout << "set conjecture " << mk_pp(n->get_data()->post(), m)
 | 
				
			||||||
 | 
					                         << " at level " << n->get_data()->level() << "\n";);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Decide global guidance based on lemma
 | 
				
			||||||
 | 
					void lemma_global_generalizer::generalize(lemma_ref &lemma) {
 | 
				
			||||||
 | 
					    // -- pob that the lemma blocks
 | 
				
			||||||
 | 
					    pob_ref &pob = lemma->get_pob();
 | 
				
			||||||
 | 
					    // -- cluster that the lemma belongs to
 | 
				
			||||||
 | 
					    lemma_cluster *cluster = pob->pt().clstr_match(lemma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Lemma does not belong to any cluster. return
 | 
				
			||||||
 | 
					    if (!cluster) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // if the cluster does not have enough gas, stop local generalization
 | 
				
			||||||
 | 
					    // and return
 | 
				
			||||||
 | 
					    if (cluster->get_gas() == 0) {
 | 
				
			||||||
 | 
					        m_st.m_num_cls_ofg++;
 | 
				
			||||||
 | 
					        pob->disable_local_gen();
 | 
				
			||||||
 | 
					        TRACE("global", tout << "stop local generalization on pob "
 | 
				
			||||||
 | 
					                             << mk_pp(pob->post(), m) << " id is "
 | 
				
			||||||
 | 
					                             << pob->post()->get_id() << "\n";);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // -- local cluster that includes the new lemma
 | 
				
			||||||
 | 
					    lemma_cluster lc(*cluster);
 | 
				
			||||||
 | 
					    // XXX most of the time lemma clustering happens before generalization
 | 
				
			||||||
 | 
					    // XXX so `add_lemma` is likely to return false, but this does not mean
 | 
				
			||||||
 | 
					    // XXX that the lemma is not new
 | 
				
			||||||
 | 
					    bool is_new = lc.add_lemma(lemma, true);
 | 
				
			||||||
 | 
					    (void)is_new;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const expr_ref &pat = lc.get_pattern();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("global", {
 | 
				
			||||||
 | 
					        tout << "Global generalization of:\n"
 | 
				
			||||||
 | 
					             << mk_and(lemma->get_cube()) << "\n"
 | 
				
			||||||
 | 
					             << "at lvl: " << lemma->level() << "\n"
 | 
				
			||||||
 | 
					             << (is_new ? "new" : "old") << "\n"
 | 
				
			||||||
 | 
					             << "Using cluster:\n"
 | 
				
			||||||
 | 
					             << pat << "\n"
 | 
				
			||||||
 | 
					             << "Existing lemmas in the cluster:\n";
 | 
				
			||||||
 | 
					        for (const auto &li : cluster->get_lemmas()) {
 | 
				
			||||||
 | 
					            tout << mk_and(li.get_lemma()->get_cube())
 | 
				
			||||||
 | 
					                 << " lvl:" << li.get_lemma()->level() << "\n";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Concretize
 | 
				
			||||||
 | 
					    if (has_nonlinear_var_mul(pat, m)) {
 | 
				
			||||||
 | 
					        m_st.m_num_non_lin++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TRACE("global",
 | 
				
			||||||
 | 
					              tout << "Found non linear pattern. Marked to concretize \n";);
 | 
				
			||||||
 | 
					        // not constructing the concrete pob here since we need a model for
 | 
				
			||||||
 | 
					        // n->post()
 | 
				
			||||||
 | 
					        pob->set_concretize_pattern(pat);
 | 
				
			||||||
 | 
					        pob->set_concretize(true);
 | 
				
			||||||
 | 
					        pob->set_gas(cluster->get_pob_gas());
 | 
				
			||||||
 | 
					        cluster->dec_gas();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Conjecture
 | 
				
			||||||
 | 
					    expr_ref lit(m);
 | 
				
			||||||
 | 
					    if (find_unique_mono_var_lit(pat, lit)) {
 | 
				
			||||||
 | 
					        // Create a conjecture by dropping literal from pob.
 | 
				
			||||||
 | 
					        TRACE("global", tout << "Conjecture with pattern\n"
 | 
				
			||||||
 | 
					                             << mk_pp(pat, m) << "\n"
 | 
				
			||||||
 | 
					                             << "with gas " << cluster->get_gas() << "\n";);
 | 
				
			||||||
 | 
					        unsigned gas = cluster->get_pob_gas();
 | 
				
			||||||
 | 
					        unsigned lvl = lc.get_min_lvl();
 | 
				
			||||||
 | 
					        if (pob) lvl = std::min(lvl, pob->level());
 | 
				
			||||||
 | 
					        if (do_conjecture(pob, lemma, lit, lvl, gas)) {
 | 
				
			||||||
 | 
					            // decrease the number of times this cluster is going to be used
 | 
				
			||||||
 | 
					            // for conjecturing
 | 
				
			||||||
 | 
					            cluster->dec_gas();
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // -- if conjecture failed, there is nothing else to do.
 | 
				
			||||||
 | 
					            // -- the pob matched pre-condition for conjecture, so it should not
 | 
				
			||||||
 | 
					            // be subsumed
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // if subsumption removed all the other lemmas, there is nothing to
 | 
				
			||||||
 | 
					    // generalize
 | 
				
			||||||
 | 
					    if (lc.get_size() < 2) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!m_do_subsume) return;
 | 
				
			||||||
 | 
					    // -- new pob that is blocked by generalized lemma
 | 
				
			||||||
 | 
					    expr_ref_vector new_post(m);
 | 
				
			||||||
 | 
					    // -- bindings for free variables of new_pob
 | 
				
			||||||
 | 
					    // -- subsumer might introduce extra free variables
 | 
				
			||||||
 | 
					    app_ref_vector bindings(lemma->get_bindings());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (m_subsumer.subsume(lc, new_post, bindings)) {
 | 
				
			||||||
 | 
					        class pob *root = pob->parent();
 | 
				
			||||||
 | 
					        while (root->parent()) root = root->parent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned new_lvl = lc.get_min_lvl();
 | 
				
			||||||
 | 
					        if (pob) new_lvl = std::min(new_lvl, pob->level());
 | 
				
			||||||
 | 
					        scoped_ptr<class pob> new_pob =
 | 
				
			||||||
 | 
					            alloc(class pob, root, pob->pt(), new_lvl, pob->depth(), false);
 | 
				
			||||||
 | 
					        if (!new_pob) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new_pob->set_desired_level(pob->level());
 | 
				
			||||||
 | 
					        new_pob->set_post(mk_and(new_post), bindings);
 | 
				
			||||||
 | 
					        new_pob->set_subsume();
 | 
				
			||||||
 | 
					        pob->set_data(new_pob.detach());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- update properties of the pob itself
 | 
				
			||||||
 | 
					        pob->set_gas(cluster->get_pob_gas() + 1);
 | 
				
			||||||
 | 
					        pob->set_expand_bnd();
 | 
				
			||||||
 | 
					        // Stop local generalization. Perhaps not the best choice in general.
 | 
				
			||||||
 | 
					        // Helped with one instance on our benchmarks
 | 
				
			||||||
 | 
					        pob->disable_local_gen();
 | 
				
			||||||
 | 
					        cluster->dec_gas();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TRACE("global", tout << "Create subsume pob at level " << new_lvl
 | 
				
			||||||
 | 
					                             << "\n"
 | 
				
			||||||
 | 
					                             << mk_and(new_post) << "\n";);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Replace bound vars in \p fml with uninterpreted constants
 | 
				
			||||||
 | 
					void lemma_global_generalizer::subsumer::ground_free_vars(expr *pat,
 | 
				
			||||||
 | 
					                                                          expr_ref &out) {
 | 
				
			||||||
 | 
					    SASSERT(!is_ground(pat));
 | 
				
			||||||
 | 
					    var_subst vs(m, false);
 | 
				
			||||||
 | 
					    // m_col_names might be bigger since it contains previously used constants
 | 
				
			||||||
 | 
					    // relying on the fact that m_col_lcm was just set. Better to compute free
 | 
				
			||||||
 | 
					    // vars of pat
 | 
				
			||||||
 | 
					    SASSERT(m_col_lcm.size() <= m_col_names.size());
 | 
				
			||||||
 | 
					    out = vs(pat, m_col_lcm.size(),
 | 
				
			||||||
 | 
					             reinterpret_cast<expr *const *>(m_col_names.data()));
 | 
				
			||||||
 | 
					    SASSERT(is_ground(out));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pob *lemma_global_generalizer::mk_concretize_pob(pob &n, model_ref &model) {
 | 
				
			||||||
 | 
					    expr_ref_vector new_post(m);
 | 
				
			||||||
 | 
					    spacer::pob_concretizer proc(m, model, n.get_concretize_pattern());
 | 
				
			||||||
 | 
					    if (proc.apply(n.post(), new_post)) {
 | 
				
			||||||
 | 
					        pob *new_pob = n.pt().mk_pob(n.parent(), n.level(), n.depth(),
 | 
				
			||||||
 | 
					                                     mk_and(new_post), n.get_binding());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TRACE("concretize", tout << "pob:\n"
 | 
				
			||||||
 | 
					                                 << mk_pp(n.post(), m)
 | 
				
			||||||
 | 
					                                 << " is concretized into:\n"
 | 
				
			||||||
 | 
					                                 << mk_pp(new_pob->post(), m) << "\n";);
 | 
				
			||||||
 | 
					        return new_pob;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pob *lemma_global_generalizer::mk_subsume_pob(pob &n) {
 | 
				
			||||||
 | 
					    if (!(n.get_gas() >= 0 && n.has_data() && n.get_data()->is_subsume()))
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pob *data = n.get_data();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pob *f = n.pt().find_pob(data->parent(), data->post());
 | 
				
			||||||
 | 
					    if (f && (f->is_in_queue() || f->is_closed())) {
 | 
				
			||||||
 | 
					        n.reset_data();
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE("global", tout << "mk_subsume_pob at level " << data->level()
 | 
				
			||||||
 | 
					                         << " with post state:\n"
 | 
				
			||||||
 | 
					                         << mk_pp(data->post(), m) << "\n";);
 | 
				
			||||||
 | 
					    f = n.pt().mk_pob(data->parent(), data->level(), data->depth(),
 | 
				
			||||||
 | 
					                      data->post(), n.get_binding());
 | 
				
			||||||
 | 
					    f->set_subsume();
 | 
				
			||||||
 | 
					    f->inherit(*data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    n.reset_data();
 | 
				
			||||||
 | 
					    return f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pob *lemma_global_generalizer::mk_conjecture_pob(pob &n) {
 | 
				
			||||||
 | 
					    if (!(n.has_data() && n.get_data()->is_conjecture() && n.get_gas() > 0))
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pob *data = n.get_data();
 | 
				
			||||||
 | 
					    pob *f = n.pt().find_pob(data->parent(), data->post());
 | 
				
			||||||
 | 
					    if (f && (f->is_in_queue() || f->is_closed())) {
 | 
				
			||||||
 | 
					        n.reset_data();
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    f = n.pt().mk_pob(data->parent(), data->level(), data->depth(),
 | 
				
			||||||
 | 
					                      data->post(), {m});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // inherit all metadata from new_pob
 | 
				
			||||||
 | 
					    f->inherit(*data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    n.reset_data();
 | 
				
			||||||
 | 
					    return f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_global_generalizer::subsumer::collect_statistics(
 | 
				
			||||||
 | 
					    statistics &st) const {
 | 
				
			||||||
 | 
					    st.update("SPACER num no over approximate", m_st.m_num_no_ovr_approx);
 | 
				
			||||||
 | 
					    st.update("SPACER num sync cvx cls", m_st.m_num_syn_cls);
 | 
				
			||||||
 | 
					    st.update("SPACER num mbp failed", m_st.m_num_mbp_failed);
 | 
				
			||||||
 | 
					    // m_cvx_closure.collect_statistics(st);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void lemma_global_generalizer::collect_statistics(statistics &st) const {
 | 
				
			||||||
 | 
					    st.update("time.spacer.solve.reach.gen.global", m_st.watch.get_seconds());
 | 
				
			||||||
 | 
					    st.update("SPACER cluster out of gas", m_st.m_num_cls_ofg);
 | 
				
			||||||
 | 
					    st.update("SPACER num non lin", m_st.m_num_non_lin);
 | 
				
			||||||
 | 
					    st.update("SPACER num cant abstract", m_st.m_num_cant_abs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										176
									
								
								src/muz/spacer/spacer_global_generalizer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/muz/spacer/spacer_global_generalizer.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,176 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  spacer_global_generalizer.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Global Guidance for Spacer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Hari Govind V K
 | 
				
			||||||
 | 
					  Arie Gurfinkel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_convex_closure.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Global guided generalization
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// See Hari Govind et al. Global Guidance for Local Generalization in Model
 | 
				
			||||||
 | 
					/// Checking. CAV 2020
 | 
				
			||||||
 | 
					class lemma_global_generalizer : public lemma_generalizer {
 | 
				
			||||||
 | 
					    /// Subsumption strategy
 | 
				
			||||||
 | 
					    class subsumer {
 | 
				
			||||||
 | 
					        struct stats {
 | 
				
			||||||
 | 
					            unsigned m_num_syn_cls;
 | 
				
			||||||
 | 
					            unsigned m_num_mbp_failed;
 | 
				
			||||||
 | 
					            unsigned m_num_no_ovr_approx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            stopwatch watch;
 | 
				
			||||||
 | 
					            stats() { reset(); }
 | 
				
			||||||
 | 
					            void reset() {
 | 
				
			||||||
 | 
					                watch.reset();
 | 
				
			||||||
 | 
					                m_num_syn_cls = 0;
 | 
				
			||||||
 | 
					                m_num_mbp_failed = 0;
 | 
				
			||||||
 | 
					                m_num_no_ovr_approx = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        stats m_st;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ast_manager &m;
 | 
				
			||||||
 | 
					        arith_util m_arith;
 | 
				
			||||||
 | 
					        bv_util m_bv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // boolean variables used as local tags
 | 
				
			||||||
 | 
					        app_ref_vector m_tags;
 | 
				
			||||||
 | 
					        // number of tags currently used
 | 
				
			||||||
 | 
					        unsigned m_used_tags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // save fresh constants for mbp
 | 
				
			||||||
 | 
					        app_ref_vector m_col_names;
 | 
				
			||||||
 | 
					        vector<rational> m_col_lcm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // create pob without free vars
 | 
				
			||||||
 | 
					        bool m_ground_pob;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Local solver to get model for computing mbp and to check whether
 | 
				
			||||||
 | 
					        // cvx_cls  ==> mbp
 | 
				
			||||||
 | 
					        ref<solver> m_solver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Return a fresh boolean variable
 | 
				
			||||||
 | 
					        app *mk_fresh_tag();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Returns false if subsumption is not supported for given cluster
 | 
				
			||||||
 | 
					        bool is_handled(const lemma_cluster &lc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Find a representative for \p c
 | 
				
			||||||
 | 
					        expr *find_repr(const model_ref &mdl, const app *c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Skolemize m_dim_frsh_cnsts in \p f
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// \p cnsts is appended with ground terms from \p mdl
 | 
				
			||||||
 | 
					        void skolemize_for_quic3(expr_ref &f, const model_ref &mdl,
 | 
				
			||||||
 | 
					                                 app_ref_vector &cnsts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Create new vars to compute convex cls
 | 
				
			||||||
 | 
					        void mk_col_names(const lemma_cluster &lc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void setup_cvx_closure(convex_closure &cc, const lemma_cluster &lc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Make \p fml ground using m_dim_frsh_cnsts. Store result in \p out
 | 
				
			||||||
 | 
					        void ground_free_vars(expr *fml, expr_ref &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Weaken \p a such that (and a) overapproximates \p b
 | 
				
			||||||
 | 
					        bool over_approximate(expr_ref_vector &a, const expr_ref b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool find_model(const expr_ref_vector &cc,
 | 
				
			||||||
 | 
					                        const expr_ref_vector &alphas, expr *bg,
 | 
				
			||||||
 | 
					                        model_ref &out_model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool is_numeral(const expr *e, rational &n) {
 | 
				
			||||||
 | 
					            return m_arith.is_numeral(e, n) || m_bv.is_numeral(e, n);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expr *mk_rat_mul(rational n, expr *v) {
 | 
				
			||||||
 | 
					            if (n.is_one()) return v;
 | 
				
			||||||
 | 
					            return m_arith.mk_mul(m_arith.mk_numeral(n, m_arith.is_int(v)), v);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      public:
 | 
				
			||||||
 | 
					        subsumer(ast_manager &m, bool ground_pob);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void collect_statistics(statistics &st) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// Compute a cube \p res such that \neg p subsumes all the lemmas in \p
 | 
				
			||||||
 | 
					        /// lc
 | 
				
			||||||
 | 
					        ///
 | 
				
			||||||
 | 
					        /// \p cnsts is a set of constants that can be used to make \p res
 | 
				
			||||||
 | 
					        /// ground
 | 
				
			||||||
 | 
					        bool subsume(const lemma_cluster &lc, expr_ref_vector &res,
 | 
				
			||||||
 | 
					                     app_ref_vector &cnsts);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct stats {
 | 
				
			||||||
 | 
					        unsigned m_num_cls_ofg;
 | 
				
			||||||
 | 
					        unsigned m_num_syn_cls;
 | 
				
			||||||
 | 
					        unsigned m_num_mbp_failed;
 | 
				
			||||||
 | 
					        unsigned m_num_non_lin;
 | 
				
			||||||
 | 
					        unsigned m_num_no_ovr_approx;
 | 
				
			||||||
 | 
					        unsigned m_num_cant_abs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stopwatch watch;
 | 
				
			||||||
 | 
					        stats() { reset(); }
 | 
				
			||||||
 | 
					        void reset() {
 | 
				
			||||||
 | 
					            watch.reset();
 | 
				
			||||||
 | 
					            m_num_cls_ofg = 0;
 | 
				
			||||||
 | 
					            m_num_non_lin = 0;
 | 
				
			||||||
 | 
					            m_num_syn_cls = 0;
 | 
				
			||||||
 | 
					            m_num_mbp_failed = 0;
 | 
				
			||||||
 | 
					            m_num_no_ovr_approx = 0;
 | 
				
			||||||
 | 
					            m_num_cant_abs = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    stats m_st;
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    subsumer m_subsumer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Decide global guidance based on lemma
 | 
				
			||||||
 | 
					    void generalize(lemma_ref &lemma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Attempt to set a conjecture on pob \p n.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// Done by dropping literal \p lit from
 | 
				
			||||||
 | 
					    /// post of \p n. \p lvl is level for conjecture pob. \p gas is the gas for
 | 
				
			||||||
 | 
					    /// the conjecture pob returns true if conjecture is set
 | 
				
			||||||
 | 
					  bool do_conjecture(pob_ref &n, lemma_ref &lemma, const expr_ref &lit, unsigned lvl,
 | 
				
			||||||
 | 
					                       unsigned gas);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Enable/disable subsume rule
 | 
				
			||||||
 | 
					    bool m_do_subsume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    lemma_global_generalizer(context &ctx);
 | 
				
			||||||
 | 
					    ~lemma_global_generalizer() override {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void operator()(lemma_ref &lemma) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void collect_statistics(statistics &st) const override;
 | 
				
			||||||
 | 
					    void reset_statistics() override { m_st.reset(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // post-actions for pobs produced during generalization
 | 
				
			||||||
 | 
					    pob *mk_concretize_pob(pob &n, model_ref &model);
 | 
				
			||||||
 | 
					    pob *mk_subsume_pob(pob &n);
 | 
				
			||||||
 | 
					    pob *mk_conjecture_pob(pob &n);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
							
								
								
									
										303
									
								
								src/muz/spacer/spacer_ind_lemma_generalizer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								src/muz/spacer/spacer_ind_lemma_generalizer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,303 @@
 | 
				
			||||||
 | 
					#include "ast/expr_functors.h"
 | 
				
			||||||
 | 
					#include "muz/spacer/spacer_context.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace spacer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class contains_array_op_proc : public i_expr_pred {
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    family_id m_array_fid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    contains_array_op_proc(ast_manager &manager)
 | 
				
			||||||
 | 
					        : m(manager), m_array_fid(array_util(m).get_family_id()) {}
 | 
				
			||||||
 | 
					    bool operator()(expr *e) override {
 | 
				
			||||||
 | 
					        return is_app(e) && to_app(e)->get_family_id() == m_array_fid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class lemma_inductive_generalizer : public lemma_generalizer {
 | 
				
			||||||
 | 
					    struct stats {
 | 
				
			||||||
 | 
					        unsigned count;
 | 
				
			||||||
 | 
					        unsigned weaken_success;
 | 
				
			||||||
 | 
					        unsigned weaken_fail;
 | 
				
			||||||
 | 
					        stopwatch watch;
 | 
				
			||||||
 | 
					        stats() { reset(); }
 | 
				
			||||||
 | 
					        void reset() {
 | 
				
			||||||
 | 
					            count = 0;
 | 
				
			||||||
 | 
					            weaken_success = 0;
 | 
				
			||||||
 | 
					            weaken_fail = 0;
 | 
				
			||||||
 | 
					            watch.reset();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ast_manager &m;
 | 
				
			||||||
 | 
					    expr_ref m_true;
 | 
				
			||||||
 | 
					    stats m_st;
 | 
				
			||||||
 | 
					    bool m_only_array_eligible;
 | 
				
			||||||
 | 
					    bool m_enable_litweak;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    contains_array_op_proc m_contains_array_op;
 | 
				
			||||||
 | 
					    check_pred m_contains_array_pred;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref_vector m_pinned;
 | 
				
			||||||
 | 
					    lemma *m_lemma = nullptr;
 | 
				
			||||||
 | 
					    spacer::pred_transformer *m_pt = nullptr;
 | 
				
			||||||
 | 
					    unsigned m_weakness = 0;
 | 
				
			||||||
 | 
					    unsigned m_level = 0;
 | 
				
			||||||
 | 
					    ptr_vector<expr> m_cube;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // temporary vector
 | 
				
			||||||
 | 
					    expr_ref_vector m_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    lemma_inductive_generalizer(spacer::context &ctx,
 | 
				
			||||||
 | 
					                                bool only_array_eligible = false,
 | 
				
			||||||
 | 
					                                bool enable_literal_weakening = true)
 | 
				
			||||||
 | 
					        : lemma_generalizer(ctx), m(ctx.get_ast_manager()),
 | 
				
			||||||
 | 
					          m_true(m.mk_true(), m), m_only_array_eligible(only_array_eligible),
 | 
				
			||||||
 | 
					          m_enable_litweak(enable_literal_weakening), m_contains_array_op(m),
 | 
				
			||||||
 | 
					          m_contains_array_pred(m_contains_array_op, m),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          m_pinned(m), m_core(m) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private:
 | 
				
			||||||
 | 
					    // -- true if literal \p lit is eligible to be generalized
 | 
				
			||||||
 | 
					    bool is_eligible(expr *lit) {
 | 
				
			||||||
 | 
					        return !m_only_array_eligible || has_arrays(lit);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool has_arrays(expr *lit) { return m_contains_array_op(lit); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void reset() {
 | 
				
			||||||
 | 
					        m_cube.reset();
 | 
				
			||||||
 | 
					        m_weakness = 0;
 | 
				
			||||||
 | 
					        m_level = 0;
 | 
				
			||||||
 | 
					        m_pt = nullptr;
 | 
				
			||||||
 | 
					        m_pinned.reset();
 | 
				
			||||||
 | 
					        m_core.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setup(lemma_ref &lemma) {
 | 
				
			||||||
 | 
					        // check that we start in uninitialized state
 | 
				
			||||||
 | 
					        SASSERT(m_pt == nullptr);
 | 
				
			||||||
 | 
					        m_lemma = lemma.get();
 | 
				
			||||||
 | 
					        m_pt = &lemma->get_pob()->pt();
 | 
				
			||||||
 | 
					        m_weakness = lemma->weakness();
 | 
				
			||||||
 | 
					        m_level = lemma->level();
 | 
				
			||||||
 | 
					        auto &cube = lemma->get_cube();
 | 
				
			||||||
 | 
					        m_cube.reset();
 | 
				
			||||||
 | 
					        for (auto *lit : cube) { m_cube.push_back(lit); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // loads current generalization from m_cube to m_core
 | 
				
			||||||
 | 
					    void load_cube_to_core() {
 | 
				
			||||||
 | 
					        m_core.reset();
 | 
				
			||||||
 | 
					        for (unsigned i = 0, sz = m_cube.size(); i < sz; ++i) {
 | 
				
			||||||
 | 
					            auto *lit = m_cube.get(i);
 | 
				
			||||||
 | 
					            if (lit == m_true) continue;
 | 
				
			||||||
 | 
					            m_core.push_back(lit);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // returns true if m_cube is inductive
 | 
				
			||||||
 | 
					    bool is_cube_inductive() {
 | 
				
			||||||
 | 
					        load_cube_to_core();
 | 
				
			||||||
 | 
					        if (m_core.empty()) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned used_level;
 | 
				
			||||||
 | 
					        if (m_pt->check_inductive(m_level, m_core, used_level, m_weakness)) {
 | 
				
			||||||
 | 
					            m_level = std::max(m_level, used_level);
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // intersect m_cube with m_core
 | 
				
			||||||
 | 
					    unsigned update_cube_by_core(unsigned from = 0) {
 | 
				
			||||||
 | 
					        // generalize away all literals in m_cube that are not in m_core
 | 
				
			||||||
 | 
					        // do not assume anything about order of literals in m_core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned success = 0;
 | 
				
			||||||
 | 
					        // mark core
 | 
				
			||||||
 | 
					        ast_fast_mark2 marked_core;
 | 
				
			||||||
 | 
					        for (auto *v : m_core) { marked_core.mark(v); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // replace unmarked literals by m_true in m_cube
 | 
				
			||||||
 | 
					        for (unsigned i = from, sz = m_cube.size(); i < sz; ++i) {
 | 
				
			||||||
 | 
					            auto *lit = m_cube.get(i);
 | 
				
			||||||
 | 
					            if (lit == m_true) continue;
 | 
				
			||||||
 | 
					            if (!marked_core.is_marked(lit)) {
 | 
				
			||||||
 | 
					                m_cube[i] = m_true;
 | 
				
			||||||
 | 
					                success++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return success;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // generalizes m_core and removes from m_cube all generalized literals
 | 
				
			||||||
 | 
					    unsigned generalize_core(unsigned from = 0) {
 | 
				
			||||||
 | 
					        unsigned success = 0;
 | 
				
			||||||
 | 
					        unsigned used_level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- while it is possible that a single literal can be generalized to
 | 
				
			||||||
 | 
					        // false,
 | 
				
			||||||
 | 
					        // -- it is fairly unlikely. Thus, we give up generalizing in this case.
 | 
				
			||||||
 | 
					        if (m_core.empty()) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- check whether candidate in m_core is inductive
 | 
				
			||||||
 | 
					        if (m_pt->check_inductive(m_level, m_core, used_level, m_weakness)) {
 | 
				
			||||||
 | 
					            success += update_cube_by_core(from);
 | 
				
			||||||
 | 
					            // update m_level to the largest level at which the the current
 | 
				
			||||||
 | 
					            // candidate in m_cube is inductive
 | 
				
			||||||
 | 
					            m_level = std::max(m_level, used_level);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return success;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // generalizes (i.e., drops) a specific literal of m_cube
 | 
				
			||||||
 | 
					    unsigned generalize1(unsigned lit_idx) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!is_eligible(m_cube.get(lit_idx))) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- populate m_core with all literals except the one being generalized
 | 
				
			||||||
 | 
					        m_core.reset();
 | 
				
			||||||
 | 
					        for (unsigned i = 0, sz = m_cube.size(); i < sz; ++i) {
 | 
				
			||||||
 | 
					            auto *lit = m_cube.get(i);
 | 
				
			||||||
 | 
					            if (lit == m_true || i == lit_idx) continue;
 | 
				
			||||||
 | 
					            m_core.push_back(lit);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return generalize_core(lit_idx);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // generalizes all literals of m_cube in a given range
 | 
				
			||||||
 | 
					    unsigned generalize_range(unsigned from, unsigned to) {
 | 
				
			||||||
 | 
					        unsigned success = 0;
 | 
				
			||||||
 | 
					        for (unsigned i = from; i < to; ++i) { success += generalize1(i); }
 | 
				
			||||||
 | 
					        return success;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // weakens a given literal of m_cube
 | 
				
			||||||
 | 
					    // weakening replaces a literal by a weaker literal(s)
 | 
				
			||||||
 | 
					    // for example, x=y might get weakened into one of x<=y or y<=x
 | 
				
			||||||
 | 
					    unsigned weaken1(unsigned lit_idx) {
 | 
				
			||||||
 | 
					        if (!is_eligible(m_cube.get(lit_idx))) return 0;
 | 
				
			||||||
 | 
					        if (m_cube.get(lit_idx) == m_true) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned success = 0;
 | 
				
			||||||
 | 
					        unsigned cube_sz = m_cube.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- save literal to be generalized, and replace it by true
 | 
				
			||||||
 | 
					        expr *saved_lit = m_cube.get(lit_idx);
 | 
				
			||||||
 | 
					        m_cube[lit_idx] = m_true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- add new weaker literals to end of m_cube and attempt to generalize
 | 
				
			||||||
 | 
					        expr_ref_vector weakening(m);
 | 
				
			||||||
 | 
					        weakening.push_back(saved_lit);
 | 
				
			||||||
 | 
					        expand_literals(m, weakening);
 | 
				
			||||||
 | 
					        if (weakening.get(0) != saved_lit) {
 | 
				
			||||||
 | 
					            for (auto *lit : weakening) {
 | 
				
			||||||
 | 
					                m_cube.push_back(lit);
 | 
				
			||||||
 | 
					                m_pinned.push_back(lit);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (m_cube.size() - cube_sz >= 2) {
 | 
				
			||||||
 | 
					                // normal case: generalize new weakening
 | 
				
			||||||
 | 
					                success += generalize_range(cube_sz, m_cube.size());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // special case -- weaken literal by another literal, check that
 | 
				
			||||||
 | 
					                // cube is still inductive
 | 
				
			||||||
 | 
					                success += (is_cube_inductive() ? 1 : 0);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- failed to generalize, restore removed literal and m_cube
 | 
				
			||||||
 | 
					        if (success == 0) {
 | 
				
			||||||
 | 
					            m_cube[lit_idx] = saved_lit;
 | 
				
			||||||
 | 
					            m_cube.shrink(cube_sz);
 | 
				
			||||||
 | 
					            m_st.weaken_fail++;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            m_st.weaken_success++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return success;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // weakens literals of m_cube in a given range
 | 
				
			||||||
 | 
					    unsigned weaken_range(unsigned from, unsigned to) {
 | 
				
			||||||
 | 
					        unsigned success = 0;
 | 
				
			||||||
 | 
					        for (unsigned i = from; i < to; ++i) { success += weaken1(i); }
 | 
				
			||||||
 | 
					        return success;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public:
 | 
				
			||||||
 | 
					    // entry point for generalization
 | 
				
			||||||
 | 
					    void operator()(lemma_ref &lemma) override {
 | 
				
			||||||
 | 
					        if (lemma->get_cube().empty()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_st.count++;
 | 
				
			||||||
 | 
					        scoped_watch _w_(m_st.watch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setup(lemma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        unsigned num_gens = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- first round -- generalize by dropping literals
 | 
				
			||||||
 | 
					        num_gens += generalize_range(0, m_cube.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // -- if weakening is enabled, start next round
 | 
				
			||||||
 | 
					        if (m_enable_litweak) {
 | 
				
			||||||
 | 
					            unsigned cube_sz = m_cube.size();
 | 
				
			||||||
 | 
					            // -- second round -- weaken literals that cannot be dropped
 | 
				
			||||||
 | 
					            num_gens += weaken_range(0, cube_sz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // -- third round -- weaken literals produced in prev round
 | 
				
			||||||
 | 
					            if (cube_sz < m_cube.size())
 | 
				
			||||||
 | 
					                num_gens += weaken_range(cube_sz, m_cube.size());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // if there is at least one generalization, update lemma
 | 
				
			||||||
 | 
					        if (num_gens > 0) {
 | 
				
			||||||
 | 
					            TRACE("indgen",
 | 
				
			||||||
 | 
					                  tout << "Generalized " << num_gens << " literals\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // reuse m_core since it is not needed for anything else
 | 
				
			||||||
 | 
					            m_core.reset();
 | 
				
			||||||
 | 
					            for (auto *lit : m_cube) {
 | 
				
			||||||
 | 
					                if (lit != m_true) m_core.push_back(lit);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TRACE("indgen", tout << "Original: " << lemma->get_cube() << "\n"
 | 
				
			||||||
 | 
					                                 << "Generalized: " << m_core << "\n";);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lemma->update_cube(lemma->get_pob(), m_core);
 | 
				
			||||||
 | 
					            lemma->set_level(m_level);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void collect_statistics(statistics &st) const override {
 | 
				
			||||||
 | 
					        st.update("time.spacer.solve.reach.gen.ind", m_st.watch.get_seconds());
 | 
				
			||||||
 | 
					        st.update("SPACER inductive gen", m_st.count);
 | 
				
			||||||
 | 
					        st.update("SPACER inductive gen weaken success", m_st.weaken_success);
 | 
				
			||||||
 | 
					        st.update("SPACER inductive gen weaken fail", m_st.weaken_fail);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    void reset_statistics() override { m_st.reset(); }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					lemma_generalizer *
 | 
				
			||||||
 | 
					alloc_lemma_inductive_generalizer(spacer::context &ctx,
 | 
				
			||||||
 | 
					                                  bool only_array_eligible,
 | 
				
			||||||
 | 
					                                  bool enable_literal_weakening) {
 | 
				
			||||||
 | 
					    return alloc(lemma_inductive_generalizer, ctx, only_array_eligible,
 | 
				
			||||||
 | 
					                 enable_literal_weakening);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					@ -1,191 +0,0 @@
 | 
				
			||||||
/**++
 | 
					 | 
				
			||||||
Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Module Name:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    spacer_json.cpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Abstract:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SPACER json marshalling support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Author:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Matteo Marescotti
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Notes:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <iomanip>
 | 
					 | 
				
			||||||
#include "spacer_context.h"
 | 
					 | 
				
			||||||
#include "spacer_json.h"
 | 
					 | 
				
			||||||
#include "spacer_util.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace spacer {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mk_epp pp = mk_epp(t, m);
 | 
					 | 
				
			||||||
    std::ostringstream ss;
 | 
					 | 
				
			||||||
    ss << pp;
 | 
					 | 
				
			||||||
    out << "\"";
 | 
					 | 
				
			||||||
    for (auto &c:ss.str()) {
 | 
					 | 
				
			||||||
        switch (c) {
 | 
					 | 
				
			||||||
        case '"':
 | 
					 | 
				
			||||||
            out << "\\\"";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case '\\':
 | 
					 | 
				
			||||||
            out << "\\\\";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case '\b':
 | 
					 | 
				
			||||||
            out << "\\b";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case '\f':
 | 
					 | 
				
			||||||
            out << "\\f";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case '\n':
 | 
					 | 
				
			||||||
            out << "\\n";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case '\r':
 | 
					 | 
				
			||||||
            out << "\\r";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case '\t':
 | 
					 | 
				
			||||||
            out << "\\t";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
            if ('\x00' <= c && c <= '\x1f') {
 | 
					 | 
				
			||||||
                out << "\\u"
 | 
					 | 
				
			||||||
                    << std::hex << std::setw(4) << std::setfill('0') << (int) c;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                out << c;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    out << "\"";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::ostream &json_marshal(std::ostream &out, lemma *l) {
 | 
					 | 
				
			||||||
    out << "{"
 | 
					 | 
				
			||||||
        << R"("init_level":")" << l->init_level()
 | 
					 | 
				
			||||||
        << R"(", "level":")" << l->level()
 | 
					 | 
				
			||||||
        << R"(", "expr":)";
 | 
					 | 
				
			||||||
    json_marshal(out, l->get_expr(), l->get_ast_manager());
 | 
					 | 
				
			||||||
    out << "}";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        std::ostringstream ls;
 | 
					 | 
				
			||||||
        for (auto l:lemmas) {
 | 
					 | 
				
			||||||
            ls << ((unsigned)ls.tellp() == 0 ? "" : ",");
 | 
					 | 
				
			||||||
            json_marshal(ls, l);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        out << "[" << ls.str() << "]";
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void json_marshaller::register_lemma(lemma *l) {
 | 
					 | 
				
			||||||
    if (l->has_pob()) {
 | 
					 | 
				
			||||||
        m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void json_marshaller::register_pob(pob *p) {
 | 
					 | 
				
			||||||
    m_relations[p];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void json_marshaller::marshal_lemmas_old(std::ostream &out) const {
 | 
					 | 
				
			||||||
    unsigned pob_id = 0;
 | 
					 | 
				
			||||||
    for (auto &pob_map:m_relations) {
 | 
					 | 
				
			||||||
        std::ostringstream pob_lemmas;
 | 
					 | 
				
			||||||
        for (auto &depth_lemmas : pob_map.second) {
 | 
					 | 
				
			||||||
            pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",")
 | 
					 | 
				
			||||||
                       << "\"" << depth_lemmas.first << "\":";
 | 
					 | 
				
			||||||
            json_marshal(pob_lemmas, depth_lemmas.second);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (pob_lemmas.tellp()) {
 | 
					 | 
				
			||||||
            out << ((unsigned)out.tellp() == 0 ? "" : ",\n");
 | 
					 | 
				
			||||||
            out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        pob_id++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
void json_marshaller::marshal_lemmas_new(std::ostream &out) const {
 | 
					 | 
				
			||||||
    unsigned pob_id = 0;
 | 
					 | 
				
			||||||
    for (auto &pob_map:m_relations) {
 | 
					 | 
				
			||||||
        std::ostringstream pob_lemmas;
 | 
					 | 
				
			||||||
        pob *n = pob_map.first;
 | 
					 | 
				
			||||||
        unsigned i = 0;
 | 
					 | 
				
			||||||
        for (auto *l : n->lemmas()) {
 | 
					 | 
				
			||||||
            pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",")
 | 
					 | 
				
			||||||
                       << "\"" << i++ << "\":";
 | 
					 | 
				
			||||||
            lemma_ref_vector lemmas_vec;
 | 
					 | 
				
			||||||
            lemmas_vec.push_back(l);
 | 
					 | 
				
			||||||
            json_marshal(pob_lemmas, lemmas_vec);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (pob_lemmas.tellp()) {
 | 
					 | 
				
			||||||
            out << ((unsigned)out.tellp() == 0 ? "" : ",\n");
 | 
					 | 
				
			||||||
            out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        pob_id++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::ostream &json_marshaller::marshal(std::ostream &out) const {
 | 
					 | 
				
			||||||
    std::ostringstream nodes;
 | 
					 | 
				
			||||||
    std::ostringstream edges;
 | 
					 | 
				
			||||||
    std::ostringstream lemmas;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (m_old_style)
 | 
					 | 
				
			||||||
        marshal_lemmas_old(lemmas);
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        marshal_lemmas_new(lemmas);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned pob_id = 0;
 | 
					 | 
				
			||||||
    unsigned depth = 0;
 | 
					 | 
				
			||||||
    while (true) {
 | 
					 | 
				
			||||||
        double root_expand_time = m_ctx->get_root().get_expand_time(depth);
 | 
					 | 
				
			||||||
        bool a = false;
 | 
					 | 
				
			||||||
        pob_id = 0;
 | 
					 | 
				
			||||||
        for (auto &pob_map:m_relations) {
 | 
					 | 
				
			||||||
            pob *n = pob_map.first;
 | 
					 | 
				
			||||||
            double expand_time = n->get_expand_time(depth);
 | 
					 | 
				
			||||||
            if (expand_time > 0) {
 | 
					 | 
				
			||||||
                a = true;
 | 
					 | 
				
			||||||
                std::ostringstream pob_expr;
 | 
					 | 
				
			||||||
                json_marshal(pob_expr, n->post(), n->get_ast_manager());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") <<
 | 
					 | 
				
			||||||
                    "{\"id\":\"" << depth << n <<
 | 
					 | 
				
			||||||
                    "\",\"relative_time\":\"" << expand_time / root_expand_time <<
 | 
					 | 
				
			||||||
                    "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time <<
 | 
					 | 
				
			||||||
                    "\",\"predicate\":\"" << n->pt().head()->get_name() <<
 | 
					 | 
				
			||||||
                    "\",\"expr_id\":\"" << n->post()->get_id() <<
 | 
					 | 
				
			||||||
                    "\",\"pob_id\":\"" << pob_id <<
 | 
					 | 
				
			||||||
                    "\",\"depth\":\"" << depth <<
 | 
					 | 
				
			||||||
                    "\",\"expr\":" << pob_expr.str() << "}";
 | 
					 | 
				
			||||||
                if (n->parent()) {
 | 
					 | 
				
			||||||
                    edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") <<
 | 
					 | 
				
			||||||
                        "{\"from\":\"" << depth << n->parent() <<
 | 
					 | 
				
			||||||
                        "\",\"to\":\"" << depth << n << "\"}";
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            pob_id++;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!a) {
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        depth++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n";
 | 
					 | 
				
			||||||
    out << "\"edges\":[\n" << edges.str() << "\n],\n";
 | 
					 | 
				
			||||||
    out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n";
 | 
					 | 
				
			||||||
    return out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,59 +0,0 @@
 | 
				
			||||||
/**++
 | 
					 | 
				
			||||||
Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Module Name:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    spacer_json.h
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Abstract:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    SPACER json marshalling support
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Author:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Matteo Marescotti
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Notes:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
--*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include<ostream>
 | 
					 | 
				
			||||||
#include<map>
 | 
					 | 
				
			||||||
#include "util/ref.h"
 | 
					 | 
				
			||||||
#include "util/ref_vector.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ast;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ast_manager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace spacer {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class lemma;
 | 
					 | 
				
			||||||
typedef sref_vector<lemma> lemma_ref_vector;
 | 
					 | 
				
			||||||
class context;
 | 
					 | 
				
			||||||
class pob;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class json_marshaller {
 | 
					 | 
				
			||||||
    context *m_ctx;
 | 
					 | 
				
			||||||
    bool m_old_style;
 | 
					 | 
				
			||||||
    std::map<pob*, std::map<unsigned, lemma_ref_vector>> m_relations;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void marshal_lemmas_old(std::ostream &out) const;
 | 
					 | 
				
			||||||
    void marshal_lemmas_new(std::ostream &out) const;
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
    json_marshaller(context *ctx, bool old_style = false) :
 | 
					 | 
				
			||||||
        m_ctx(ctx), m_old_style(old_style)  {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void register_lemma(lemma *l);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void register_pob(pob *p);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::ostream &marshal(std::ostream &out) const;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -17,143 +17,169 @@ Revision History:
 | 
				
			||||||
--*/
 | 
					--*/
 | 
				
			||||||
#include "muz/spacer/spacer_matrix.h"
 | 
					#include "muz/spacer/spacer_matrix.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace spacer
 | 
					namespace spacer {
 | 
				
			||||||
{
 | 
					spacer_matrix::spacer_matrix(unsigned m, unsigned n)
 | 
				
			||||||
    spacer_matrix::spacer_matrix(unsigned m, unsigned n) : m_num_rows(m), m_num_cols(n)
 | 
					    : m_num_rows(m), m_num_cols(n) {
 | 
				
			||||||
    {
 | 
					    m_matrix.reserve(m_num_rows);
 | 
				
			||||||
        for (unsigned i=0; i < m; ++i)
 | 
					    for (unsigned i = 0; i < m_num_rows; ++i) {
 | 
				
			||||||
        {
 | 
					        m_matrix[i].reserve(m_num_cols, rational(0));
 | 
				
			||||||
            vector<rational> v;
 | 
					    }
 | 
				
			||||||
            for (unsigned j=0; j < n; ++j)
 | 
					}
 | 
				
			||||||
            {
 | 
					
 | 
				
			||||||
                v.push_back(rational(0));
 | 
					void spacer_matrix::get_col(unsigned i, vector<rational> &row) const {
 | 
				
			||||||
 | 
					    SASSERT(i < m_num_cols);
 | 
				
			||||||
 | 
					    row.reset();
 | 
				
			||||||
 | 
					    row.reserve(m_num_rows);
 | 
				
			||||||
 | 
					    unsigned j = 0;
 | 
				
			||||||
 | 
					    for (auto &v : m_matrix) { row[j++] = (v.get(i)); }
 | 
				
			||||||
 | 
					    SASSERT(row.size() == m_num_rows);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void spacer_matrix::add_row(const vector<rational> &row) {
 | 
				
			||||||
 | 
					    SASSERT(row.size() == m_num_cols);
 | 
				
			||||||
 | 
					    m_matrix.push_back(row);
 | 
				
			||||||
 | 
					    m_num_rows = m_matrix.size();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            m_matrix.push_back(v);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unsigned spacer_matrix::num_rows()
 | 
					        if (max.is_zero()) // skip this column
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return m_num_rows;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsigned spacer_matrix::num_cols()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return m_num_cols;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const 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, const 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
 | 
					            ++j;
 | 
				
			||||||
            rational max = m_matrix[i][j];
 | 
					        } else {
 | 
				
			||||||
            unsigned max_index = i;
 | 
					            // reorder rows if necessary
 | 
				
			||||||
 | 
					            vector<rational> tmp = m_matrix[i];
 | 
				
			||||||
 | 
					            m_matrix[i] = m_matrix[max_index];
 | 
				
			||||||
 | 
					            m_matrix[max_index] = m_matrix[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (unsigned k=i+1; k < m_matrix.size(); ++k)
 | 
					            // normalize row
 | 
				
			||||||
            {
 | 
					            rational pivot = m_matrix[i][j];
 | 
				
			||||||
                if (max < m_matrix[k][j])
 | 
					            if (!pivot.is_one()) {
 | 
				
			||||||
                {
 | 
					                for (unsigned k = 0; k < m_matrix[i].size(); ++k) {
 | 
				
			||||||
                    max = m_matrix[k][j];
 | 
					                    m_matrix[i][k] = m_matrix[i][k] / pivot;
 | 
				
			||||||
                    max_index = k;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (max.is_zero()) // skip this column
 | 
					            // subtract row from all other rows
 | 
				
			||||||
            {
 | 
					            for (unsigned k = 1; k < m_matrix.size(); ++k) {
 | 
				
			||||||
                ++j;
 | 
					                if (k != i) {
 | 
				
			||||||
            }
 | 
					                    rational factor = m_matrix[k][j];
 | 
				
			||||||
            else
 | 
					                    for (unsigned l = 0; l < m_matrix[k].size(); ++l) {
 | 
				
			||||||
            {
 | 
					                        m_matrix[k][l] =
 | 
				
			||||||
                // reorder rows if necessary
 | 
					                            m_matrix[k][l] - (factor * m_matrix[i][l]);
 | 
				
			||||||
                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)
 | 
					            ++i;
 | 
				
			||||||
        {
 | 
					            ++j;
 | 
				
			||||||
            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()
 | 
					    if (get_verbosity_level() >= 1) { SASSERT(m_matrix.size() > 0); }
 | 
				
			||||||
    {
 | 
					
 | 
				
			||||||
        verbose_stream() << "\nMatrix\n";
 | 
					    return i; // i points to the row after the last row which is non-zero
 | 
				
			||||||
        for (const auto& row : m_matrix)
 | 
					}
 | 
				
			||||||
        {
 | 
					
 | 
				
			||||||
            for (const auto& element : row)
 | 
					std::ostream &spacer_matrix::display(std::ostream &out) const {
 | 
				
			||||||
            {
 | 
					    out << "Matrix\n";
 | 
				
			||||||
                verbose_stream() << element << ", ";
 | 
					    for (const auto &row : m_matrix) {
 | 
				
			||||||
            }
 | 
					        for (const auto &element : row) { out << element << ", "; }
 | 
				
			||||||
            verbose_stream() << "\n";
 | 
					        out << "\n";
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        verbose_stream() << "\n";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    void spacer_matrix::normalize()
 | 
					    out << "\n";
 | 
				
			||||||
    {
 | 
					    return out;
 | 
				
			||||||
        rational den = rational::one();
 | 
					}
 | 
				
			||||||
        for (unsigned i=0; i < m_num_rows; ++i)
 | 
					
 | 
				
			||||||
        {
 | 
					void spacer_matrix::normalize() {
 | 
				
			||||||
            for (unsigned j=0; j < m_num_cols; ++j)
 | 
					    rational den = rational::one();
 | 
				
			||||||
            {
 | 
					    for (unsigned i = 0; i < m_num_rows; ++i) {
 | 
				
			||||||
                den = lcm(den, denominator(m_matrix[i][j]));
 | 
					        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)
 | 
					    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];
 | 
					            m_matrix[i][j] = den * m_matrix[i][j];
 | 
				
			||||||
                SASSERT(m_matrix[i][j].is_int());
 | 
					            SASSERT(m_matrix[i][j].is_int());
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// attempt to guess that all rows of the matrix are linearly dependent
 | 
				
			||||||
 | 
					bool spacer_matrix::is_lin_reltd(unsigned i, unsigned j, rational &coeff1,
 | 
				
			||||||
 | 
					                                 rational &coeff2, rational &off) const {
 | 
				
			||||||
 | 
					    SASSERT(m_num_rows > 1);
 | 
				
			||||||
 | 
					    coeff1 = m_matrix[0][j] - m_matrix[1][j];
 | 
				
			||||||
 | 
					    coeff2 = m_matrix[1][i] - m_matrix[0][i];
 | 
				
			||||||
 | 
					    off = (m_matrix[0][i] * m_matrix[1][j]) - (m_matrix[1][i] * m_matrix[0][j]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned k = 0; k < m_num_rows; k++) {
 | 
				
			||||||
 | 
					        if (((coeff1 * m_matrix[k][i]) + (coeff2 * m_matrix[k][j]) + off) !=
 | 
				
			||||||
 | 
					            rational::zero()) {
 | 
				
			||||||
 | 
					            TRACE("cvx_dbg_verb",
 | 
				
			||||||
 | 
					                  tout << "Didn't work for " << m_matrix[k][i] << " and "
 | 
				
			||||||
 | 
					                       << m_matrix[k][j] << " with coefficients " << coeff1
 | 
				
			||||||
 | 
					                       << " , " << coeff2 << " and offset " << off << "\n";);
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rational div = gcd(coeff1, gcd(coeff2, off));
 | 
				
			||||||
 | 
					    if (div == 0) return false;
 | 
				
			||||||
 | 
					    coeff1 = coeff1 / div;
 | 
				
			||||||
 | 
					    coeff2 = coeff2 / div;
 | 
				
			||||||
 | 
					    off = off / div;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool spacer_matrix::compute_linear_deps(spacer_matrix &eq) const {
 | 
				
			||||||
 | 
					    SASSERT(m_num_rows > 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eq.reset(m_num_cols + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    rational coeff1, coeff2, off;
 | 
				
			||||||
 | 
					    vector<rational> lin_dep;
 | 
				
			||||||
 | 
					    lin_dep.reserve(m_num_cols + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned i = 0; i < m_num_cols; i++) {
 | 
				
			||||||
 | 
					        for (unsigned j = i + 1; j < m_num_cols; j++) {
 | 
				
			||||||
 | 
					            if (is_lin_reltd(i, j, coeff1, coeff2, off)) {
 | 
				
			||||||
 | 
					                SASSERT(!(coeff1 == 0 && coeff2 == 0 && off == 0));
 | 
				
			||||||
 | 
					                lin_dep[i] = coeff1;
 | 
				
			||||||
 | 
					                lin_dep[j] = coeff2;
 | 
				
			||||||
 | 
					                lin_dep[m_num_cols] = off;
 | 
				
			||||||
 | 
					                eq.add_row(lin_dep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TRACE("cvx_dbg_verb", {
 | 
				
			||||||
 | 
					                    tout << "Adding row ";
 | 
				
			||||||
 | 
					                    for (rational r : lin_dep) tout << r << " ";
 | 
				
			||||||
 | 
					                    tout << "\n";
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                // reset everything
 | 
				
			||||||
 | 
					                lin_dep[i] = rational::zero();
 | 
				
			||||||
 | 
					                lin_dep[j] = rational::zero();
 | 
				
			||||||
 | 
					                lin_dep[m_num_cols] = 0;
 | 
				
			||||||
 | 
					                // Found a dependency for this row, move on.
 | 
				
			||||||
 | 
					                // sound because of transitivity of is_lin_reltd
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return eq.num_rows() > 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,24 +22,44 @@ Revision History:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace spacer {
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class spacer_matrix {
 | 
					class spacer_matrix {
 | 
				
			||||||
    public:
 | 
					  private:
 | 
				
			||||||
        spacer_matrix(unsigned m, unsigned n); // m rows, n columns
 | 
					    unsigned m_num_rows;
 | 
				
			||||||
 | 
					    unsigned m_num_cols;
 | 
				
			||||||
 | 
					    vector<vector<rational>> m_matrix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        unsigned num_rows();
 | 
					    bool is_lin_reltd(unsigned i, unsigned j, rational &coeff1,
 | 
				
			||||||
        unsigned num_cols();
 | 
					                      rational &coeff2, rational &off) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const rational& get(unsigned i, unsigned j);
 | 
					  public:
 | 
				
			||||||
        void set(unsigned i, unsigned j, const rational& v);
 | 
					    spacer_matrix(unsigned m, unsigned n); // m rows, n columns
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        unsigned perform_gaussian_elimination();
 | 
					    unsigned num_rows() const { return m_num_rows; }
 | 
				
			||||||
 | 
					    unsigned num_cols() const { return m_num_cols; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void print_matrix();
 | 
					    const rational &get(unsigned i, unsigned j) const { return m_matrix[i][j]; }
 | 
				
			||||||
        void normalize();
 | 
					    void set(unsigned i, unsigned j, const rational &v) { m_matrix[i][j] = v; }
 | 
				
			||||||
    private:
 | 
					 | 
				
			||||||
        unsigned m_num_rows;
 | 
					 | 
				
			||||||
        unsigned m_num_cols;
 | 
					 | 
				
			||||||
        vector<vector<rational>> m_matrix;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const vector<rational> &get_row(unsigned i) const {
 | 
				
			||||||
 | 
					        SASSERT(i < num_rows());
 | 
				
			||||||
 | 
					        return m_matrix.get(i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns a copy of row \p i
 | 
				
			||||||
 | 
					    void get_col(unsigned i, vector<rational> &row) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void add_row(const vector<rational> &row);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void reset(unsigned n_cols) {
 | 
				
			||||||
 | 
					        m_num_rows = 0;
 | 
				
			||||||
 | 
					        m_num_cols = n_cols;
 | 
				
			||||||
 | 
					        m_matrix.reset();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ostream &display(std::ostream &out) const;
 | 
				
			||||||
 | 
					    void normalize();
 | 
				
			||||||
 | 
					    unsigned perform_gaussian_elimination();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool compute_linear_deps(spacer_matrix &eq) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -284,6 +284,11 @@ namespace spacer {
 | 
				
			||||||
                                   ptr_buffer<proof> const &parents,
 | 
					                                   ptr_buffer<proof> const &parents,
 | 
				
			||||||
                                   unsigned num_params,
 | 
					                                   unsigned num_params,
 | 
				
			||||||
                                   parameter const *params) {
 | 
					                                   parameter const *params) {
 | 
				
			||||||
 | 
					        if(num_params != parents.size() + 1) {
 | 
				
			||||||
 | 
					            //TODO: fix bug
 | 
				
			||||||
 | 
					            TRACE("spacer.fkab", tout << "UNEXPECTED INPUT TO FUNCTION. Bailing out\n";);
 | 
				
			||||||
 | 
					            return proof_ref(m);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        SASSERT(num_params == parents.size() + 1 /* one param is missing */);
 | 
					        SASSERT(num_params == parents.size() + 1 /* one param is missing */);
 | 
				
			||||||
        arith_util a(m);
 | 
					        arith_util a(m);
 | 
				
			||||||
        th_rewriter rw(m);
 | 
					        th_rewriter rw(m);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@ bool sem_matcher::operator()(expr * e1, expr * e2, substitution & s, bool &pos)
 | 
				
			||||||
        top = false;
 | 
					        top = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (n1->get_decl() != n2->get_decl()) {
 | 
					        if (n1->get_decl() != n2->get_decl()) {
 | 
				
			||||||
            expr *e1 = nullptr, *e2 = nullptr;
 | 
					            expr *e1 = nullptr, *e2 = nullptr, *e3 = nullptr, *e4 = nullptr, *e5 = nullptr;
 | 
				
			||||||
            rational val1, val2;
 | 
					            rational val1, val2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // x<=y == !(x>y)
 | 
					            // x<=y == !(x>y)
 | 
				
			||||||
| 
						 | 
					@ -120,6 +120,26 @@ bool sem_matcher::operator()(expr * e1, expr * e2, substitution & s, bool &pos)
 | 
				
			||||||
            else {
 | 
					            else {
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					            // x >= var and !(y <= n)
 | 
				
			||||||
 | 
					            // match (x, y) and (var, n+1)
 | 
				
			||||||
 | 
					            if (m_arith.is_ge(n1, e1, e2) && is_var(e2) &&
 | 
				
			||||||
 | 
					                m.is_not(n2, e3) && m_arith.is_le(e3, e4, e5) &&
 | 
				
			||||||
 | 
					                m_arith.is_int(e5) &&
 | 
				
			||||||
 | 
					                m_arith.is_numeral(e5, val2)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                expr* num2 = m_arith.mk_numeral(val2 + 1, true);
 | 
				
			||||||
 | 
					                m_pinned.push_back(num2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!match_var(to_var(e2), num2)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                m_todo.pop_back();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                m_todo.push_back(expr_pair(e1, e4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        unsigned num_args1 = n1->get_num_args();
 | 
					        unsigned num_args1 = n1->get_num_args();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,11 +38,11 @@ class sem_matcher {
 | 
				
			||||||
    substitution *        m_subst;
 | 
					    substitution *        m_subst;
 | 
				
			||||||
    svector<expr_pair>    m_todo;
 | 
					    svector<expr_pair>    m_todo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void reset();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool match_var(var *v, expr *e);
 | 
					    bool match_var(var *v, expr *e);
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    sem_matcher(ast_manager &man);
 | 
					    sem_matcher(ast_manager &man);
 | 
				
			||||||
 | 
					    void reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
       \brief Return true if e2 is an instance of e1.
 | 
					       \brief Return true if e2 is an instance of e1.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -396,8 +396,8 @@ namespace spacer {
 | 
				
			||||||
                matrix.set(i, map[pair.second], pair.first);
 | 
					                matrix.set(i, map[pair.second], pair.first);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        matrix.print_matrix();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        IF_VERBOSE(10, matrix.display(verbose_stream()););
 | 
				
			||||||
        // 3. normalize matrix to integer values
 | 
					        // 3. normalize matrix to integer values
 | 
				
			||||||
        matrix.normalize();
 | 
					        matrix.normalize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -21,126 +21,163 @@ Revision History:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "ast/array_decl_plugin.h"
 | 
				
			||||||
#include "ast/ast.h"
 | 
					#include "ast/ast.h"
 | 
				
			||||||
#include "ast/ast_pp.h"
 | 
					#include "ast/ast_pp.h"
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include "ast/bv_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "ast/expr_map.h"
 | 
				
			||||||
 | 
					#include "model/model.h"
 | 
				
			||||||
#include "util/obj_hashtable.h"
 | 
					#include "util/obj_hashtable.h"
 | 
				
			||||||
#include "util/ref_vector.h"
 | 
					#include "util/ref_vector.h"
 | 
				
			||||||
#include "util/trace.h"
 | 
					#include "util/trace.h"
 | 
				
			||||||
#include "util/vector.h"
 | 
					#include "util/vector.h"
 | 
				
			||||||
#include "ast/arith_decl_plugin.h"
 | 
					 | 
				
			||||||
#include "ast/array_decl_plugin.h"
 | 
					 | 
				
			||||||
#include "ast/bv_decl_plugin.h"
 | 
					 | 
				
			||||||
#include "ast/ast_util.h"
 | 
					 | 
				
			||||||
#include "ast/expr_map.h"
 | 
					 | 
				
			||||||
#include "model/model.h"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "util/stopwatch.h"
 | 
					 | 
				
			||||||
#include "muz/spacer/spacer_antiunify.h"
 | 
					#include "muz/spacer/spacer_antiunify.h"
 | 
				
			||||||
 | 
					#include "util/stopwatch.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class model;
 | 
					class model;
 | 
				
			||||||
class model_core;
 | 
					class model_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace spacer {
 | 
					namespace spacer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inline unsigned infty_level () {
 | 
					inline unsigned infty_level() { return UINT_MAX; }
 | 
				
			||||||
        return UINT_MAX;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inline bool is_infty_level(unsigned lvl) {
 | 
					inline bool is_infty_level(unsigned lvl) {
 | 
				
			||||||
        // XXX: level is 16 bits in class pob
 | 
					    // XXX: level is 16 bits in class pob
 | 
				
			||||||
        return lvl >= 65535;
 | 
					    return lvl >= 65535;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    typedef ptr_vector<app> app_vector;
 | 
					 | 
				
			||||||
    typedef ptr_vector<func_decl> decl_vector;
 | 
					 | 
				
			||||||
    typedef obj_hashtable<func_decl> func_decl_set;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
       \brief hoist non-boolean if expressions.
 | 
					 | 
				
			||||||
    */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void to_mbp_benchmark(std::ostream &out, const expr* fml, const app_ref_vector &vars);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TBD: deprecate by qe::mbp
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 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, model &mdl,
 | 
					 | 
				
			||||||
                     bool reduce_all_selects=false,
 | 
					 | 
				
			||||||
                     bool native_mbp=false,
 | 
					 | 
				
			||||||
                     bool dont_sub=false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // deprecate
 | 
					 | 
				
			||||||
    void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml,
 | 
					 | 
				
			||||||
                     model_ref& M, expr_map& map);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TBD: sort out
 | 
					 | 
				
			||||||
    void expand_literals(ast_manager &m, expr_ref_vector& conjs);
 | 
					 | 
				
			||||||
    expr_ref_vector compute_implicant_literals(model &mdl,
 | 
					 | 
				
			||||||
                                    expr_ref_vector &formula);
 | 
					 | 
				
			||||||
    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. On return, out is the resulting expression, and vars is
 | 
					 | 
				
			||||||
     * a map from variable ids to corresponding skolem constants.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void mbqi_project(model &mdl, 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);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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 = nullptr);
 | 
					 | 
				
			||||||
        void rw(expr *e, expr_ref &out);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool is_clause(ast_manager &m, expr *n); 
 | 
					 | 
				
			||||||
    bool is_literal(ast_manager &m, expr *n);
 | 
					 | 
				
			||||||
    bool is_atom(ast_manager &m, expr *n);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // set f to true in model
 | 
					 | 
				
			||||||
    void set_true_in_mdl(model &model, func_decl *f);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef ptr_vector<app> app_vector;
 | 
				
			||||||
 | 
					typedef ptr_vector<func_decl> decl_vector;
 | 
				
			||||||
 | 
					typedef obj_hashtable<func_decl> func_decl_set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					   \brief hoist non-boolean if expressions.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void to_mbp_benchmark(std::ostream &out, const expr *fml,
 | 
				
			||||||
 | 
					                      const app_ref_vector &vars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TBD: deprecate by qe::mbp
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 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, model &mdl,
 | 
				
			||||||
 | 
					                bool reduce_all_selects = false, bool native_mbp = false,
 | 
				
			||||||
 | 
					                bool dont_sub = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// deprecate
 | 
				
			||||||
 | 
					void qe_project(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
 | 
				
			||||||
 | 
					                model_ref &M, expr_map &map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TBD: sort out
 | 
				
			||||||
 | 
					void expand_literals(ast_manager &m, expr_ref_vector &conjs);
 | 
				
			||||||
 | 
					expr_ref_vector compute_implicant_literals(model &mdl,
 | 
				
			||||||
 | 
					                                           expr_ref_vector &formula);
 | 
				
			||||||
 | 
					void simplify_bounds(expr_ref_vector &lemmas);
 | 
				
			||||||
 | 
					bool is_normalized(expr_ref e, bool use_simplify_bounds = true,
 | 
				
			||||||
 | 
					                   bool factor_eqs = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true,
 | 
				
			||||||
 | 
					               bool factor_eqs = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void normalize_order(expr *e, expr_ref &out);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Ground expression by replacing all free variables by skolem
 | 
				
			||||||
 | 
					 * constants. On return, out is the resulting expression, and vars is
 | 
				
			||||||
 | 
					 * a map from variable ids to corresponding skolem constants.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mbqi_project(model &mdl, 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 = nullptr);
 | 
				
			||||||
 | 
					    void rw(expr *e, expr_ref &out);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool is_clause(ast_manager &m, expr *n);
 | 
				
			||||||
 | 
					bool is_literal(ast_manager &m, expr *n);
 | 
				
			||||||
 | 
					bool is_atom(ast_manager &m, expr *n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// set f to true in model
 | 
				
			||||||
 | 
					void set_true_in_mdl(model &model, func_decl *f);
 | 
				
			||||||
 | 
					/// Returns number of free variables in \p e
 | 
				
			||||||
 | 
					unsigned get_num_vars(expr *e);
 | 
				
			||||||
 | 
					// Return all uninterpreted constants of \p q
 | 
				
			||||||
 | 
					void collect_uninterp_consts(expr *a, expr_ref_vector &out);
 | 
				
			||||||
 | 
					bool has_nonlinear_mul(expr *e, ast_manager &m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Returns true if \p e contains a multiplication a variable by not-a-number
 | 
				
			||||||
 | 
					bool has_nonlinear_var_mul(expr *e, ast_manager &m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// check whether lit is an instance of mono_var_pattern
 | 
				
			||||||
 | 
					bool is_mono_var(expr *lit, ast_manager &m, arith_util &a_util);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// a mono_var_pattern has only one variable in the whole expression and is
 | 
				
			||||||
 | 
					// linear. lit is the literal with the variable
 | 
				
			||||||
 | 
					bool find_unique_mono_var_lit(const expr_ref &p, expr_ref &lit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Drop all literals that numerically match \p lit, from \p fml_vec.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// \p abs_fml holds the result. Returns true if any literal has been dropped
 | 
				
			||||||
 | 
					bool filter_out_lit(const expr_ref_vector &in, const expr_ref &lit,
 | 
				
			||||||
 | 
					                    expr_ref_vector &out);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Returns true if range of s is numeric
 | 
				
			||||||
 | 
					bool is_numeric_sub(const substitution &s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Returns true if \p e contains \p mod
 | 
				
			||||||
 | 
					bool contains_mod(const expr_ref &e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Returns true if \p e contains a real-valued sub-term
 | 
				
			||||||
 | 
					bool contains_real(const expr_ref &e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// multiply fml with num and simplify rationals to ints
 | 
				
			||||||
 | 
					// fml should be in LIA/LRA/Arrays
 | 
				
			||||||
 | 
					// assumes that fml is a sum of products
 | 
				
			||||||
 | 
					void mul_by_rat(expr_ref &fml, rational num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spacer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -286,22 +286,23 @@ namespace nlsat {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            bool check_invariant() const {
 | 
					            bool check_invariant() const {
 | 
				
			||||||
                SASSERT(m_sections.size() == m_sorted_sections.size());
 | 
					                DEBUG_CODE(
 | 
				
			||||||
                for (unsigned i = 0; i < m_sorted_sections.size(); i++) {
 | 
					                    SASSERT(m_sections.size() == m_sorted_sections.size());
 | 
				
			||||||
                    SASSERT(m_sorted_sections[i] < m_sections.size());
 | 
					                    for (unsigned i = 0; i < m_sorted_sections.size(); i++) {
 | 
				
			||||||
                    SASSERT(m_sections[m_sorted_sections[i]].m_pos == i);
 | 
					                        SASSERT(m_sorted_sections[i] < m_sections.size());
 | 
				
			||||||
                }
 | 
					                        SASSERT(m_sections[m_sorted_sections[i]].m_pos == i);
 | 
				
			||||||
                unsigned total_num_sections = 0;
 | 
					                    }
 | 
				
			||||||
                unsigned total_num_signs = 0;
 | 
					                    unsigned total_num_sections = 0;
 | 
				
			||||||
                for (unsigned i = 0; i < m_info.size(); i++) {
 | 
					                    unsigned total_num_signs = 0;
 | 
				
			||||||
                    SASSERT(m_info[i].m_first_section <= m_poly_sections.size());
 | 
					                    for (unsigned i = 0; i < m_info.size(); i++) {
 | 
				
			||||||
                    SASSERT(m_info[i].m_num_roots == 0 || m_info[i].m_first_section < m_poly_sections.size());
 | 
					                        SASSERT(m_info[i].m_first_section <= m_poly_sections.size());
 | 
				
			||||||
                    SASSERT(m_info[i].m_first_sign < m_poly_signs.size());
 | 
					                        SASSERT(m_info[i].m_num_roots == 0 || m_info[i].m_first_section < m_poly_sections.size());
 | 
				
			||||||
                    total_num_sections += m_info[i].m_num_roots;
 | 
					                        SASSERT(m_info[i].m_first_sign < m_poly_signs.size());
 | 
				
			||||||
                    total_num_signs += m_info[i].m_num_roots + 1;
 | 
					                        total_num_sections += m_info[i].m_num_roots;
 | 
				
			||||||
                }
 | 
					                        total_num_signs += m_info[i].m_num_roots + 1;
 | 
				
			||||||
                SASSERT(total_num_sections == m_poly_sections.size());
 | 
					                    }
 | 
				
			||||||
                SASSERT(total_num_signs == m_poly_signs.size());
 | 
					                    SASSERT(total_num_sections == m_poly_sections.size());
 | 
				
			||||||
 | 
					                    SASSERT(total_num_signs == m_poly_signs.size()););
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ def_module_params('solver',
 | 
				
			||||||
                          ('cancel_backup_file', SYMBOL, '', "file to save partial search state if search is canceled"),
 | 
					                          ('cancel_backup_file', SYMBOL, '', "file to save partial search state if search is canceled"),
 | 
				
			||||||
                          ('timeout', UINT, UINT_MAX, "timeout on the solver object; overwrites a global timeout"),
 | 
					                          ('timeout', UINT, UINT_MAX, "timeout on the solver object; overwrites a global timeout"),
 | 
				
			||||||
                          ('lemmas2console', BOOL, False, 'print lemmas during search'),
 | 
					                          ('lemmas2console', BOOL, False, 'print lemmas during search'),
 | 
				
			||||||
 | 
					                          ('instantiations2console', BOOL, False, 'print quantifier instantiations to the console'),
 | 
				
			||||||
                          ('axioms2files', BOOL, False, 'print negated theory axioms to separate files during search'),
 | 
					                          ('axioms2files', BOOL, False, 'print negated theory axioms to separate files during search'),
 | 
				
			||||||
                          ))
 | 
					                          ))
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -376,7 +376,7 @@ namespace mbp {
 | 
				
			||||||
                    rhs = m_bv.mk_concat(2, args);
 | 
					                    rhs = m_bv.mk_concat(2, args);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (lo > 0 && hi + 1 == sz) {
 | 
					                else if (lo > 0 && hi + 1 == sz) {
 | 
				
			||||||
                    expr* args[3] = { rhs, m_bv.mk_extract(lo - 1, 0, e) };
 | 
					                    expr* args[2] = { rhs, m_bv.mk_extract(lo - 1, 0, e) };
 | 
				
			||||||
                    rhs = m_bv.mk_concat(2, args);                    
 | 
					                    rhs = m_bv.mk_concat(2, args);                    
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else {
 | 
					                else {
 | 
				
			||||||
| 
						 | 
					@ -384,6 +384,15 @@ namespace mbp {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					            expr *f = nullptr
 | 
				
			||||||
 | 
					            // this one is for Nuno to find and generalize for invertible expressions
 | 
				
			||||||
 | 
					            if (m_bv.is_bv_add(lhs, e, f) && is_variable(e)) {
 | 
				
			||||||
 | 
					                lhs = e;
 | 
				
			||||||
 | 
					                rhs = m_bv.mk_bv_sub(rhs, f);
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    public:
 | 
					    public:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -269,8 +269,6 @@ public:
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool validate_model(model& model, expr_ref_vector const& fmls) {
 | 
					    bool validate_model(model& model, expr_ref_vector const& fmls) {
 | 
				
			||||||
        expr_ref val(m);
 | 
					 | 
				
			||||||
        model_evaluator eval(model);
 | 
					 | 
				
			||||||
        for (expr* f : fmls) {
 | 
					        for (expr* f : fmls) {
 | 
				
			||||||
            VERIFY(!model.is_false(f));
 | 
					            VERIFY(!model.is_false(f));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,7 @@ z3_add_component(sat
 | 
				
			||||||
    util
 | 
					    util
 | 
				
			||||||
    dd
 | 
					    dd
 | 
				
			||||||
    grobner
 | 
					    grobner
 | 
				
			||||||
 | 
					    params
 | 
				
			||||||
  PYG_FILES
 | 
					  PYG_FILES
 | 
				
			||||||
    sat_asymm_branch_params.pyg
 | 
					    sat_asymm_branch_params.pyg
 | 
				
			||||||
    sat_params.pyg
 | 
					    sat_params.pyg
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,27 +112,6 @@ static void read_clause(Buffer & in, std::ostream& err, sat::literal_vector & li
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename Buffer>
 | 
					 | 
				
			||||||
static void read_pragma(Buffer & in, std::ostream& err, std::string& p, sat::proof_hint& h) {
 | 
					 | 
				
			||||||
    skip_whitespace(in);
 | 
					 | 
				
			||||||
    if (*in != 'p')
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    ++in;
 | 
					 | 
				
			||||||
    while (*in == ' ')
 | 
					 | 
				
			||||||
        ++in;
 | 
					 | 
				
			||||||
    while (true) {
 | 
					 | 
				
			||||||
        if (*in == EOF)
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        if (*in == '\n') {
 | 
					 | 
				
			||||||
            ++in;
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        p.push_back(*in);
 | 
					 | 
				
			||||||
        ++in;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!p.empty()) 
 | 
					 | 
				
			||||||
        h.from_string(p);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<typename Buffer>
 | 
					template<typename Buffer>
 | 
				
			||||||
| 
						 | 
					@ -177,25 +156,7 @@ namespace dimacs {
 | 
				
			||||||
    std::ostream& operator<<(std::ostream& out, drat_pp const& p) {
 | 
					    std::ostream& operator<<(std::ostream& out, drat_pp const& p) {
 | 
				
			||||||
        auto const& r = p.r;
 | 
					        auto const& r = p.r;
 | 
				
			||||||
        sat::status_pp pp(r.m_status, p.th);
 | 
					        sat::status_pp pp(r.m_status, p.th);
 | 
				
			||||||
        switch (r.m_tag) {
 | 
					        return out << pp << " " << r.m_lits << " 0\n";            
 | 
				
			||||||
        case drat_record::tag_t::is_clause:
 | 
					 | 
				
			||||||
            if (!r.m_pragma.empty()) 
 | 
					 | 
				
			||||||
                return out << pp << " " << r.m_lits << " 0 p " << r.m_pragma << "\n";
 | 
					 | 
				
			||||||
            return out << pp << " " << r.m_lits << " 0\n";            
 | 
					 | 
				
			||||||
        case drat_record::tag_t::is_node:
 | 
					 | 
				
			||||||
            return out << "e " << r.m_node_id << " " << r.m_name << " " << r.m_args << "0\n";
 | 
					 | 
				
			||||||
        case drat_record::tag_t::is_sort:
 | 
					 | 
				
			||||||
            return out << "s " << r.m_node_id << " " << r.m_name << " " << r.m_args << "0\n";
 | 
					 | 
				
			||||||
        case drat_record::tag_t::is_decl:
 | 
					 | 
				
			||||||
            return out << "f " << r.m_node_id << " " << r.m_name << " " << r.m_args << "0\n";
 | 
					 | 
				
			||||||
        case drat_record::tag_t::is_bool_def:
 | 
					 | 
				
			||||||
            return out << "b " << r.m_node_id << " " << r.m_args << "0\n";
 | 
					 | 
				
			||||||
        case drat_record::tag_t::is_var:
 | 
					 | 
				
			||||||
            return out << "v " << r.m_node_id << " " << r.m_name << " " << r.m_args << "0\n";
 | 
					 | 
				
			||||||
        case drat_record::tag_t::is_quantifier:
 | 
					 | 
				
			||||||
            return out << "q " << r.m_node_id << " " << r.m_name << " " << r.m_args << "0\n";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return out;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    char const* drat_parser::parse_identifier() {
 | 
					    char const* drat_parser::parse_identifier() {
 | 
				
			||||||
| 
						 | 
					@ -266,47 +227,10 @@ namespace dimacs {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool drat_parser::next() {
 | 
					    bool drat_parser::next() {
 | 
				
			||||||
        int n, b, e, theory_id;
 | 
					        int theory_id;
 | 
				
			||||||
        auto parse_ast = [&](drat_record::tag_t tag) {
 | 
					 | 
				
			||||||
            ++in;
 | 
					 | 
				
			||||||
            skip_whitespace(in);
 | 
					 | 
				
			||||||
            n = parse_int(in, err);                    
 | 
					 | 
				
			||||||
            skip_whitespace(in);
 | 
					 | 
				
			||||||
            m_record.m_name = parse_sexpr();
 | 
					 | 
				
			||||||
            m_record.m_tag = tag;
 | 
					 | 
				
			||||||
            m_record.m_node_id = n;
 | 
					 | 
				
			||||||
            m_record.m_args.reset();
 | 
					 | 
				
			||||||
            while (true) {
 | 
					 | 
				
			||||||
                n = parse_int(in, err);
 | 
					 | 
				
			||||||
                if (n == 0)
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                if (n < 0)
 | 
					 | 
				
			||||||
                    throw lex_error();
 | 
					 | 
				
			||||||
                m_record.m_args.push_back(n);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        auto parse_var = [&]() {
 | 
					 | 
				
			||||||
            ++in;
 | 
					 | 
				
			||||||
            skip_whitespace(in);
 | 
					 | 
				
			||||||
            n = parse_int(in, err);                    
 | 
					 | 
				
			||||||
            skip_whitespace(in);
 | 
					 | 
				
			||||||
            m_record.m_name = parse_sexpr();
 | 
					 | 
				
			||||||
            m_record.m_tag = drat_record::tag_t::is_var;
 | 
					 | 
				
			||||||
            m_record.m_node_id = n;
 | 
					 | 
				
			||||||
            m_record.m_args.reset();
 | 
					 | 
				
			||||||
            n = parse_int(in, err);
 | 
					 | 
				
			||||||
            if (n < 0)
 | 
					 | 
				
			||||||
                throw lex_error();
 | 
					 | 
				
			||||||
            m_record.m_args.push_back(n);
 | 
					 | 
				
			||||||
            n = parse_int(in, err);
 | 
					 | 
				
			||||||
            if (n != 0)
 | 
					 | 
				
			||||||
                throw lex_error();
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
        loop:
 | 
					        loop:
 | 
				
			||||||
            skip_whitespace(in);
 | 
					            skip_whitespace(in);
 | 
				
			||||||
            m_record.m_pragma.clear();
 | 
					 | 
				
			||||||
            m_record.m_hint.reset();
 | 
					 | 
				
			||||||
            switch (*in) {
 | 
					            switch (*in) {
 | 
				
			||||||
            case EOF:
 | 
					            case EOF:
 | 
				
			||||||
                return false;                
 | 
					                return false;                
 | 
				
			||||||
| 
						 | 
					@ -321,7 +245,6 @@ namespace dimacs {
 | 
				
			||||||
                ++in;
 | 
					                ++in;
 | 
				
			||||||
                skip_whitespace(in);
 | 
					                skip_whitespace(in);
 | 
				
			||||||
                read_clause(in, err, m_record.m_lits);
 | 
					                read_clause(in, err, m_record.m_lits);
 | 
				
			||||||
                m_record.m_tag = drat_record::tag_t::is_clause;
 | 
					 | 
				
			||||||
                m_record.m_status = sat::status::input();
 | 
					                m_record.m_status = sat::status::input();
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'a':
 | 
					            case 'a':
 | 
				
			||||||
| 
						 | 
					@ -331,49 +254,13 @@ namespace dimacs {
 | 
				
			||||||
                theory_id = read_theory_id();
 | 
					                theory_id = read_theory_id();
 | 
				
			||||||
                skip_whitespace(in);
 | 
					                skip_whitespace(in);
 | 
				
			||||||
                read_clause(in, err, m_record.m_lits);
 | 
					                read_clause(in, err, m_record.m_lits);
 | 
				
			||||||
                read_pragma(in, err, m_record.m_pragma, m_record.m_hint);
 | 
					 | 
				
			||||||
                m_record.m_tag = drat_record::tag_t::is_clause;
 | 
					 | 
				
			||||||
                m_record.m_status = sat::status::th(false, theory_id);
 | 
					                m_record.m_status = sat::status::th(false, theory_id);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'e':
 | 
					 | 
				
			||||||
                // parse expression definition
 | 
					 | 
				
			||||||
                parse_ast(drat_record::tag_t::is_node);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 'v':
 | 
					 | 
				
			||||||
                parse_var();
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 'q':
 | 
					 | 
				
			||||||
                parse_ast(drat_record::tag_t::is_quantifier);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 'f':
 | 
					 | 
				
			||||||
                // parse function declaration
 | 
					 | 
				
			||||||
                parse_ast(drat_record::tag_t::is_decl);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 's':
 | 
					 | 
				
			||||||
                // parse sort declaration (not used)
 | 
					 | 
				
			||||||
                parse_ast(drat_record::tag_t::is_sort);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 'b':
 | 
					 | 
				
			||||||
                // parse bridge between Boolean variable identifier b 
 | 
					 | 
				
			||||||
                // and expression identifier e, which is of type Bool
 | 
					 | 
				
			||||||
                ++in;
 | 
					 | 
				
			||||||
                skip_whitespace(in);
 | 
					 | 
				
			||||||
                b = parse_int(in, err);
 | 
					 | 
				
			||||||
                n = parse_int(in, err);
 | 
					 | 
				
			||||||
                e = parse_int(in, err);
 | 
					 | 
				
			||||||
                if (e != 0)
 | 
					 | 
				
			||||||
                    throw lex_error();
 | 
					 | 
				
			||||||
                m_record.m_tag = drat_record::tag_t::is_bool_def;
 | 
					 | 
				
			||||||
                m_record.m_node_id = b;
 | 
					 | 
				
			||||||
                m_record.m_args.reset();
 | 
					 | 
				
			||||||
                m_record.m_args.push_back(n);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            case 'd':
 | 
					            case 'd':
 | 
				
			||||||
                // parse clause deletion
 | 
					                // parse clause deletion
 | 
				
			||||||
                ++in;
 | 
					                ++in;
 | 
				
			||||||
                skip_whitespace(in);
 | 
					                skip_whitespace(in);
 | 
				
			||||||
                read_clause(in, err, m_record.m_lits);
 | 
					                read_clause(in, err, m_record.m_lits);
 | 
				
			||||||
                m_record.m_tag = drat_record::tag_t::is_clause;
 | 
					 | 
				
			||||||
                m_record.m_status = sat::status::deleted();
 | 
					                m_record.m_status = sat::status::deleted();
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case 'r':
 | 
					            case 'r':
 | 
				
			||||||
| 
						 | 
					@ -383,13 +270,11 @@ namespace dimacs {
 | 
				
			||||||
                skip_whitespace(in);
 | 
					                skip_whitespace(in);
 | 
				
			||||||
                theory_id = read_theory_id();
 | 
					                theory_id = read_theory_id();
 | 
				
			||||||
                read_clause(in, err, m_record.m_lits);
 | 
					                read_clause(in, err, m_record.m_lits);
 | 
				
			||||||
                m_record.m_tag = drat_record::tag_t::is_clause;
 | 
					 | 
				
			||||||
                m_record.m_status = sat::status::th(true, theory_id);
 | 
					                m_record.m_status = sat::status::th(true, theory_id);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                // parse clause redundant modulo DRAT (or mostly just DRUP)
 | 
					                // parse clause redundant modulo DRAT (or mostly just DRUP)
 | 
				
			||||||
                read_clause(in, err, m_record.m_lits);
 | 
					                read_clause(in, err, m_record.m_lits);
 | 
				
			||||||
                m_record.m_tag = drat_record::tag_t::is_clause;
 | 
					 | 
				
			||||||
                m_record.m_status = sat::status::redundant();
 | 
					                m_record.m_status = sat::status::redundant();
 | 
				
			||||||
                break;                
 | 
					                break;                
 | 
				
			||||||
            }    
 | 
					            }    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,18 +53,11 @@ namespace dimacs {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct drat_record {
 | 
					    struct drat_record {
 | 
				
			||||||
        enum class tag_t { is_clause, is_node, is_decl, is_sort, is_bool_def, is_var, is_quantifier };
 | 
					 | 
				
			||||||
        tag_t            m_tag{ tag_t::is_clause };
 | 
					 | 
				
			||||||
        // a clause populates m_lits and m_status
 | 
					        // a clause populates m_lits and m_status
 | 
				
			||||||
        // a node populates m_node_id, m_name, m_args
 | 
					        // a node populates m_node_id, m_name, m_args
 | 
				
			||||||
        // a bool def populates m_node_id and one element in m_args
 | 
					        // a bool def populates m_node_id and one element in m_args
 | 
				
			||||||
        sat::literal_vector  m_lits;
 | 
					        sat::literal_vector  m_lits;
 | 
				
			||||||
        sat::status     m_status = sat::status::redundant();
 | 
					        sat::status     m_status = sat::status::redundant();
 | 
				
			||||||
        unsigned        m_node_id = 0;
 | 
					 | 
				
			||||||
        std::string     m_name;
 | 
					 | 
				
			||||||
        unsigned_vector m_args;
 | 
					 | 
				
			||||||
        std::string     m_pragma;
 | 
					 | 
				
			||||||
        sat::proof_hint m_hint;
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct drat_pp {
 | 
					    struct drat_pp {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ Revision History:
 | 
				
			||||||
#include "sat/sat_types.h"
 | 
					#include "sat/sat_types.h"
 | 
				
			||||||
#include "sat/sat_params.hpp"
 | 
					#include "sat/sat_params.hpp"
 | 
				
			||||||
#include "sat/sat_simplifier_params.hpp"
 | 
					#include "sat/sat_simplifier_params.hpp"
 | 
				
			||||||
 | 
					#include "params/solver_params.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace sat {
 | 
					namespace sat {
 | 
				
			||||||
| 
						 | 
					@ -31,6 +32,8 @@ namespace sat {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void config::updt_params(params_ref const & _p) {
 | 
					    void config::updt_params(params_ref const & _p) {
 | 
				
			||||||
        sat_params p(_p);
 | 
					        sat_params p(_p);
 | 
				
			||||||
 | 
					        solver_params sp(_p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m_max_memory  = megabytes_to_bytes(p.max_memory());
 | 
					        m_max_memory  = megabytes_to_bytes(p.max_memory());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        symbol s = p.restart();
 | 
					        symbol s = p.restart();
 | 
				
			||||||
| 
						 | 
					@ -194,10 +197,10 @@ namespace sat {
 | 
				
			||||||
        m_drat_check_unsat  = p.drat_check_unsat();
 | 
					        m_drat_check_unsat  = p.drat_check_unsat();
 | 
				
			||||||
        m_drat_check_sat  = p.drat_check_sat();
 | 
					        m_drat_check_sat  = p.drat_check_sat();
 | 
				
			||||||
        m_drat_file       = p.drat_file();
 | 
					        m_drat_file       = p.drat_file();
 | 
				
			||||||
        m_drat            = (m_drat_check_unsat || m_drat_file.is_non_empty_string() || m_drat_check_sat) && p.threads() == 1;
 | 
					        m_smt_proof       = p.smt_proof();
 | 
				
			||||||
 | 
					        m_drat            = !p.drat_disable() && (sp.lemmas2console() || m_drat_check_unsat || m_drat_file.is_non_empty_string() || m_smt_proof.is_non_empty_string() || m_drat_check_sat) && p.threads() == 1;
 | 
				
			||||||
        m_drat_binary     = p.drat_binary();
 | 
					        m_drat_binary     = p.drat_binary();
 | 
				
			||||||
        m_drat_activity   = p.drat_activity();
 | 
					        m_drat_activity   = p.drat_activity();
 | 
				
			||||||
        m_drup_trim       = p.drup_trim();
 | 
					 | 
				
			||||||
        m_dyn_sub_res     = p.dyn_sub_res();
 | 
					        m_dyn_sub_res     = p.dyn_sub_res();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Parameters used in Liang, Ganesh, Poupart, Czarnecki AAAI 2016.
 | 
					        // Parameters used in Liang, Ganesh, Poupart, Czarnecki AAAI 2016.
 | 
				
			||||||
| 
						 | 
					@ -248,13 +251,9 @@ namespace sat {
 | 
				
			||||||
        m_card_solver = p.cardinality_solver();
 | 
					        m_card_solver = p.cardinality_solver();
 | 
				
			||||||
        m_xor_solver = false; // prevent users from playing with this option
 | 
					        m_xor_solver = false; // prevent users from playing with this option
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sat_simplifier_params sp(_p);
 | 
					        sat_simplifier_params ssp(_p);
 | 
				
			||||||
        m_elim_vars = sp.elim_vars();
 | 
					        m_elim_vars = ssp.elim_vars();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
        if (m_drat && (m_xor_solver || m_card_solver)) 
 | 
					 | 
				
			||||||
            throw sat_param_exception("DRAT checking only works for pure CNF");
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void config::collect_param_descrs(param_descrs & r) {
 | 
					    void config::collect_param_descrs(param_descrs & r) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -177,9 +177,9 @@ namespace sat {
 | 
				
			||||||
        bool               m_drat;
 | 
					        bool               m_drat;
 | 
				
			||||||
        bool               m_drat_binary;
 | 
					        bool               m_drat_binary;
 | 
				
			||||||
        symbol             m_drat_file;
 | 
					        symbol             m_drat_file;
 | 
				
			||||||
 | 
					        symbol             m_smt_proof;
 | 
				
			||||||
        bool               m_drat_check_unsat;
 | 
					        bool               m_drat_check_unsat;
 | 
				
			||||||
        bool               m_drat_check_sat;
 | 
					        bool               m_drat_check_sat;
 | 
				
			||||||
        bool               m_drup_trim;
 | 
					 | 
				
			||||||
        bool               m_drat_activity;
 | 
					        bool               m_drat_activity;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        bool               m_card_solver;
 | 
					        bool               m_card_solver;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,8 +52,7 @@ namespace sat {
 | 
				
			||||||
    void drat::updt_config() {            
 | 
					    void drat::updt_config() {            
 | 
				
			||||||
        m_check_unsat = s.get_config().m_drat_check_unsat;
 | 
					        m_check_unsat = s.get_config().m_drat_check_unsat;
 | 
				
			||||||
        m_check_sat   = s.get_config().m_drat_check_sat;
 | 
					        m_check_sat   = s.get_config().m_drat_check_sat;
 | 
				
			||||||
        m_trim        = s.get_config().m_drup_trim;       
 | 
					        m_check       = m_check_unsat || m_check_sat;
 | 
				
			||||||
        m_check       = m_check_unsat || m_check_sat || m_trim;
 | 
					 | 
				
			||||||
        m_activity    = s.get_config().m_drat_activity;
 | 
					        m_activity    = s.get_config().m_drat_activity;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,14 +129,6 @@ namespace sat {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        buffer[len++] = '0';
 | 
					        buffer[len++] = '0';
 | 
				
			||||||
        if (st.get_hint()) {
 | 
					 | 
				
			||||||
            buffer[len++] = ' ';
 | 
					 | 
				
			||||||
            buffer[len++] = 'p';
 | 
					 | 
				
			||||||
            buffer[len++] = ' ';
 | 
					 | 
				
			||||||
            auto* ps = st.get_hint();
 | 
					 | 
				
			||||||
            for (auto ch : ps->to_string())
 | 
					 | 
				
			||||||
                buffer[len++] = ch;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        buffer[len++] = '\n';
 | 
					        buffer[len++] = '\n';
 | 
				
			||||||
        m_out->write(buffer, len);
 | 
					        m_out->write(buffer, len);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -210,8 +201,6 @@ namespace sat {
 | 
				
			||||||
        if (st.is_redundant() && st.is_sat()) 
 | 
					        if (st.is_redundant() && st.is_sat()) 
 | 
				
			||||||
            verify(1, &l);
 | 
					            verify(1, &l);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (m_trim)
 | 
					 | 
				
			||||||
            m_proof.push_back({mk_clause(1, &l, st.is_redundant()), st});
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (st.is_deleted()) 
 | 
					        if (st.is_deleted()) 
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
| 
						 | 
					@ -230,8 +219,7 @@ namespace sat {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st););
 | 
					        IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st););
 | 
				
			||||||
        if (st.is_deleted()) {
 | 
					        if (st.is_deleted()) {
 | 
				
			||||||
            if (m_trim)
 | 
					            ;
 | 
				
			||||||
                m_proof.push_back({mk_clause(2, lits, true), st});
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
            if (st.is_redundant() && st.is_sat()) 
 | 
					            if (st.is_redundant() && st.is_sat()) 
 | 
				
			||||||
| 
						 | 
					@ -255,31 +243,6 @@ namespace sat {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void drat::bool_def(bool_var v, unsigned n) {
 | 
					 | 
				
			||||||
        if (m_out)
 | 
					 | 
				
			||||||
            (*m_out) << "b " << v << " " << n << " 0\n";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void drat::def_begin(char id, unsigned n, std::string const& name) {
 | 
					 | 
				
			||||||
        if (m_out) 
 | 
					 | 
				
			||||||
            (*m_out) << id << " " << n << " " << name;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void drat::def_add_arg(unsigned arg) {
 | 
					 | 
				
			||||||
        if (m_out)
 | 
					 | 
				
			||||||
            (*m_out) << " " << arg;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void drat::def_end() {
 | 
					 | 
				
			||||||
        if (m_out)
 | 
					 | 
				
			||||||
            (*m_out) << " 0\n";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void drat::log_adhoc(std::function<void(std::ostream&)>& fn) {
 | 
					 | 
				
			||||||
        if (m_out)
 | 
					 | 
				
			||||||
            fn(*m_out);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void drat::append(clause& c, status st) {
 | 
					    void drat::append(clause& c, status st) {
 | 
				
			||||||
        TRACE("sat_drat", pp(tout, st) << " " << c << "\n";);
 | 
					        TRACE("sat_drat", pp(tout, st) << " " << c << "\n";);
 | 
				
			||||||
        for (literal lit : c) declare(lit);
 | 
					        for (literal lit : c) declare(lit);
 | 
				
			||||||
| 
						 | 
					@ -453,6 +416,8 @@ namespace sat {
 | 
				
			||||||
    void drat::verify(unsigned n, literal const* c) {
 | 
					    void drat::verify(unsigned n, literal const* c) {
 | 
				
			||||||
        if (!m_check_unsat) 
 | 
					        if (!m_check_unsat) 
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
 | 
					        if (m_inconsistent)
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
        for (unsigned i = 0; i < n; ++i) 
 | 
					        for (unsigned i = 0; i < n; ++i) 
 | 
				
			||||||
            declare(c[i]);
 | 
					            declare(c[i]);
 | 
				
			||||||
        if (is_drup(n, c)) {
 | 
					        if (is_drup(n, c)) {
 | 
				
			||||||
| 
						 | 
					@ -683,6 +648,7 @@ namespace sat {
 | 
				
			||||||
            verify(0, nullptr);
 | 
					            verify(0, nullptr);
 | 
				
			||||||
            SASSERT(m_inconsistent);
 | 
					            SASSERT(m_inconsistent);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(0, nullptr, status::redundant());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    void drat::add(literal l, bool learned) {
 | 
					    void drat::add(literal l, bool learned) {
 | 
				
			||||||
        ++m_stats.m_num_add;
 | 
					        ++m_stats.m_num_add;
 | 
				
			||||||
| 
						 | 
					@ -690,6 +656,8 @@ namespace sat {
 | 
				
			||||||
        if (m_out) dump(1, &l, st);
 | 
					        if (m_out) dump(1, &l, st);
 | 
				
			||||||
        if (m_bout) bdump(1, &l, st);
 | 
					        if (m_bout) bdump(1, &l, st);
 | 
				
			||||||
        if (m_check) append(l, st);
 | 
					        if (m_check) append(l, st);
 | 
				
			||||||
 | 
					        TRACE("sat", tout << "add " << m_clause_eh << "\n");
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(1, &l, st);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    void drat::add(literal l1, literal l2, status st) {
 | 
					    void drat::add(literal l1, literal l2, status st) {
 | 
				
			||||||
        if (st.is_deleted())
 | 
					        if (st.is_deleted())
 | 
				
			||||||
| 
						 | 
					@ -700,6 +668,7 @@ namespace sat {
 | 
				
			||||||
        if (m_out) dump(2, ls, st);
 | 
					        if (m_out) dump(2, ls, st);
 | 
				
			||||||
        if (m_bout) bdump(2, ls, st);
 | 
					        if (m_bout) bdump(2, ls, st);
 | 
				
			||||||
        if (m_check) append(l1, l2, st);
 | 
					        if (m_check) append(l1, l2, st);
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(2, ls, st);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    void drat::add(clause& c, status st) {
 | 
					    void drat::add(clause& c, status st) {
 | 
				
			||||||
        if (st.is_deleted())
 | 
					        if (st.is_deleted())
 | 
				
			||||||
| 
						 | 
					@ -709,6 +678,7 @@ namespace sat {
 | 
				
			||||||
        if (m_out) dump(c.size(), c.begin(), st);
 | 
					        if (m_out) dump(c.size(), c.begin(), st);
 | 
				
			||||||
        if (m_bout) bdump(c.size(), c.begin(), st);
 | 
					        if (m_bout) bdump(c.size(), c.begin(), st);
 | 
				
			||||||
        if (m_check) append(mk_clause(c), st);
 | 
					        if (m_check) append(mk_clause(c), st);
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(c.size(), c.begin(), st);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    void drat::add(literal_vector const& lits, status st) {
 | 
					    void drat::add(literal_vector const& lits, status st) {
 | 
				
			||||||
| 
						 | 
					@ -722,13 +692,16 @@ namespace sat {
 | 
				
			||||||
            ++m_stats.m_num_add;
 | 
					            ++m_stats.m_num_add;
 | 
				
			||||||
        if (m_check) {
 | 
					        if (m_check) {
 | 
				
			||||||
            switch (sz) {
 | 
					            switch (sz) {
 | 
				
			||||||
            case 0: add(); break;
 | 
					            case 0: if (st.is_input()) m_inconsistent = true; else add(); break;
 | 
				
			||||||
            case 1: append(lits[0], st); break;
 | 
					            case 1: append(lits[0], st); break;
 | 
				
			||||||
            default: append(mk_clause(sz, lits, st.is_redundant()), st); break;
 | 
					            default: append(mk_clause(sz, lits, st.is_redundant()), st); break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (m_out)
 | 
					        if (m_out)
 | 
				
			||||||
            dump(sz, lits, st);
 | 
					            dump(sz, lits, st);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (m_clause_eh)
 | 
				
			||||||
 | 
					            m_clause_eh->on_clause(sz, lits, st);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void drat::add(literal_vector const& c) {
 | 
					    void drat::add(literal_vector const& c) {
 | 
				
			||||||
| 
						 | 
					@ -748,6 +721,8 @@ namespace sat {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (m_clause_eh)
 | 
				
			||||||
 | 
					            m_clause_eh->on_clause(c.size(), c.data(), status::redundant());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void drat::del(literal l) {
 | 
					    void drat::del(literal l) {
 | 
				
			||||||
| 
						 | 
					@ -755,6 +730,7 @@ namespace sat {
 | 
				
			||||||
        if (m_out) dump(1, &l, status::deleted());
 | 
					        if (m_out) dump(1, &l, status::deleted());
 | 
				
			||||||
        if (m_bout) bdump(1, &l, status::deleted());
 | 
					        if (m_bout) bdump(1, &l, status::deleted());
 | 
				
			||||||
        if (m_check) append(l, status::deleted());
 | 
					        if (m_check) append(l, status::deleted());
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(1, &l, status::deleted());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void drat::del(literal l1, literal l2) {
 | 
					    void drat::del(literal l1, literal l2) {
 | 
				
			||||||
| 
						 | 
					@ -763,6 +739,7 @@ namespace sat {
 | 
				
			||||||
        if (m_out) dump(2, ls, status::deleted());
 | 
					        if (m_out) dump(2, ls, status::deleted());
 | 
				
			||||||
        if (m_bout) bdump(2, ls, status::deleted());
 | 
					        if (m_bout) bdump(2, ls, status::deleted());
 | 
				
			||||||
        if (m_check) append(l1, l2, status::deleted());
 | 
					        if (m_check) append(l1, l2, status::deleted());
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(2, ls, status::deleted());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void drat::del(clause& c) {
 | 
					    void drat::del(clause& c) {
 | 
				
			||||||
| 
						 | 
					@ -780,7 +757,8 @@ namespace sat {
 | 
				
			||||||
        ++m_stats.m_num_del;
 | 
					        ++m_stats.m_num_del;
 | 
				
			||||||
        if (m_out) dump(c.size(), c.begin(), status::deleted());
 | 
					        if (m_out) dump(c.size(), c.begin(), status::deleted());
 | 
				
			||||||
        if (m_bout) bdump(c.size(), c.begin(), status::deleted());
 | 
					        if (m_bout) bdump(c.size(), c.begin(), status::deleted());
 | 
				
			||||||
        if (m_check) append(mk_clause(c), status::deleted());        
 | 
					        if (m_check) append(mk_clause(c), status::deleted());     
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(c.size(), c.begin(), status::deleted());   
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    clause& drat::mk_clause(clause& c) {
 | 
					    clause& drat::mk_clause(clause& c) {
 | 
				
			||||||
| 
						 | 
					@ -796,23 +774,9 @@ namespace sat {
 | 
				
			||||||
        if (m_out) dump(c.size(), c.begin(), status::deleted());
 | 
					        if (m_out) dump(c.size(), c.begin(), status::deleted());
 | 
				
			||||||
        if (m_bout) bdump(c.size(), c.begin(), status::deleted());
 | 
					        if (m_bout) bdump(c.size(), c.begin(), status::deleted());
 | 
				
			||||||
        if (m_check) append(mk_clause(c.size(), c.begin(), true), status::deleted());        
 | 
					        if (m_check) append(mk_clause(c.size(), c.begin(), true), status::deleted());        
 | 
				
			||||||
 | 
					        if (m_clause_eh) m_clause_eh->on_clause(c.size(), c.begin(), status::deleted());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    // placeholder for trim function.
 | 
					 | 
				
			||||||
    // 1. trail contains justification for the empty clause.
 | 
					 | 
				
			||||||
    // 2. backward pass to prune.
 | 
					 | 
				
			||||||
    // 
 | 
					 | 
				
			||||||
    svector<std::pair<clause&, status>> drat::trim() {
 | 
					 | 
				
			||||||
        SASSERT(m_units.empty());
 | 
					 | 
				
			||||||
        svector<std::pair<clause&, status>> proof;
 | 
					 | 
				
			||||||
        for (auto const& [c, st] : m_proof)
 | 
					 | 
				
			||||||
            if (!st.is_deleted())
 | 
					 | 
				
			||||||
                proof.push_back({c,st});
 | 
					 | 
				
			||||||
        return proof;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void drat::check_model(model const& m) {
 | 
					    void drat::check_model(model const& m) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -844,196 +808,4 @@ namespace sat {
 | 
				
			||||||
        return out;
 | 
					        return out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    std::string proof_hint::to_string() const {
 | 
					 | 
				
			||||||
        std::ostringstream ous;
 | 
					 | 
				
			||||||
        switch (m_ty) {
 | 
					 | 
				
			||||||
        case hint_type::null_h:
 | 
					 | 
				
			||||||
            return std::string();
 | 
					 | 
				
			||||||
        case hint_type::farkas_h:
 | 
					 | 
				
			||||||
            ous << "farkas ";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case hint_type::bound_h:
 | 
					 | 
				
			||||||
            ous << "bound ";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        case hint_type::implied_eq_h:
 | 
					 | 
				
			||||||
            ous << "implied_eq ";
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
            UNREACHABLE();
 | 
					 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        for (auto const& [q, l] : m_literals)
 | 
					 | 
				
			||||||
            ous << rational(q) << " * " << l << " ";
 | 
					 | 
				
			||||||
        for (auto const& [a, b] : m_eqs)
 | 
					 | 
				
			||||||
            ous << " = " << a << " " << b << " ";
 | 
					 | 
				
			||||||
        for (auto const& [a, b] : m_diseqs)
 | 
					 | 
				
			||||||
            ous << " != " << a << " " << b << " ";
 | 
					 | 
				
			||||||
        return ous.str();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void proof_hint::from_string(char const* s) {
 | 
					 | 
				
			||||||
        proof_hint& h = *this;
 | 
					 | 
				
			||||||
        h.reset();
 | 
					 | 
				
			||||||
        h.m_ty = hint_type::null_h;
 | 
					 | 
				
			||||||
        if (!s)
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        auto ws = [&]() {
 | 
					 | 
				
			||||||
            while (*s == ' ' || *s == '\n' || *s == '\t')
 | 
					 | 
				
			||||||
                ++s;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto parse_type = [&]() {
 | 
					 | 
				
			||||||
            if (0 == strncmp(s, "farkas", 6)) {
 | 
					 | 
				
			||||||
                h.m_ty = hint_type::farkas_h;
 | 
					 | 
				
			||||||
                s += 6;
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (0 == strncmp(s, "bound", 5)) {
 | 
					 | 
				
			||||||
                h.m_ty = hint_type::bound_h;
 | 
					 | 
				
			||||||
                s += 5;
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (0 == strncmp(s, "implied_eq", 10)) {
 | 
					 | 
				
			||||||
                h.m_ty = hint_type::implied_eq_h;
 | 
					 | 
				
			||||||
                s += 10;
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sbuffer<char> buff;
 | 
					 | 
				
			||||||
        auto parse_coeff = [&]() {
 | 
					 | 
				
			||||||
            buff.reset();
 | 
					 | 
				
			||||||
            while (*s && *s != ' ') {
 | 
					 | 
				
			||||||
                buff.push_back(*s);
 | 
					 | 
				
			||||||
                ++s;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            buff.push_back(0);
 | 
					 | 
				
			||||||
            return rational(buff.data());
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto parse_literal = [&]() {
 | 
					 | 
				
			||||||
            rational r = parse_coeff();
 | 
					 | 
				
			||||||
            if (!r.is_int())
 | 
					 | 
				
			||||||
                return sat::null_literal;
 | 
					 | 
				
			||||||
            if (r < 0)
 | 
					 | 
				
			||||||
                return sat::literal((-r).get_unsigned(), true);
 | 
					 | 
				
			||||||
            return sat::literal(r.get_unsigned(), false);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        auto parse_coeff_literal = [&]() {
 | 
					 | 
				
			||||||
            if (*s == '=') {
 | 
					 | 
				
			||||||
                ++s;
 | 
					 | 
				
			||||||
                ws();
 | 
					 | 
				
			||||||
                unsigned a = parse_coeff().get_unsigned();
 | 
					 | 
				
			||||||
                ws();
 | 
					 | 
				
			||||||
                unsigned b = parse_coeff().get_unsigned();
 | 
					 | 
				
			||||||
                h.m_eqs.push_back(std::make_pair(a, b));
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (*s == '!' && *(s + 1) == '=') {
 | 
					 | 
				
			||||||
                s += 2;
 | 
					 | 
				
			||||||
                ws();
 | 
					 | 
				
			||||||
                unsigned a = parse_coeff().get_unsigned();
 | 
					 | 
				
			||||||
                ws();
 | 
					 | 
				
			||||||
                unsigned b = parse_coeff().get_unsigned();
 | 
					 | 
				
			||||||
                h.m_diseqs.push_back(std::make_pair(a, b));
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            rational coeff = parse_coeff();
 | 
					 | 
				
			||||||
            ws();
 | 
					 | 
				
			||||||
            if (*s == '*') {
 | 
					 | 
				
			||||||
                ++s;
 | 
					 | 
				
			||||||
                ws();
 | 
					 | 
				
			||||||
                sat::literal lit = parse_literal();
 | 
					 | 
				
			||||||
                h.m_literals.push_back(std::make_pair(coeff, lit));
 | 
					 | 
				
			||||||
                return true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        ws();
 | 
					 | 
				
			||||||
        if (!parse_type())
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        ws();
 | 
					 | 
				
			||||||
        while (*s) {
 | 
					 | 
				
			||||||
            if (!parse_coeff_literal())
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            ws();            
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
    // debugging code
 | 
					 | 
				
			||||||
    bool drat::is_clause(clause& c, literal l1, literal l2, literal l3, drat::status st1, drat::status st2) {
 | 
					 | 
				
			||||||
        //if (st1 != st2) return false;
 | 
					 | 
				
			||||||
        if (c.size() != 3) return false;
 | 
					 | 
				
			||||||
        if (l1 == c[0]) {
 | 
					 | 
				
			||||||
            if (l2 == c[1] && l3 == c[2]) return true;
 | 
					 | 
				
			||||||
            if (l2 == c[2] && l3 == c[1]) return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (l2 == c[0]) {
 | 
					 | 
				
			||||||
            if (l1 == c[1] && l3 == c[2]) return true;
 | 
					 | 
				
			||||||
            if (l1 == c[2] && l3 == c[1]) return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (l3 == c[0]) {
 | 
					 | 
				
			||||||
            if (l1 == c[1] && l2 == c[2]) return true;
 | 
					 | 
				
			||||||
            if (l1 == c[2] && l2 == c[1]) return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if 0
 | 
					 | 
				
			||||||
        if (!m_inconsistent) {
 | 
					 | 
				
			||||||
            literal_vector lits(n, c);
 | 
					 | 
				
			||||||
            IF_VERBOSE(0, verbose_stream() << "not drup " << lits << "\n");
 | 
					 | 
				
			||||||
            for (unsigned v = 0; v < m_assignment.size(); ++v) {
 | 
					 | 
				
			||||||
                lbool val = m_assignment[v];
 | 
					 | 
				
			||||||
                if (val != l_undef) {
 | 
					 | 
				
			||||||
                    IF_VERBOSE(0, verbose_stream() << literal(v, false) << " |-> " << val << "\n");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (clause* cp : s.m_clauses) {
 | 
					 | 
				
			||||||
                clause& cl = *cp;
 | 
					 | 
				
			||||||
                bool found = false;
 | 
					 | 
				
			||||||
                for (literal l : cl) {
 | 
					 | 
				
			||||||
                    if (m_assignment[l.var()] != (l.sign() ? l_true : l_false)) {
 | 
					 | 
				
			||||||
                        found = true;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (!found) {
 | 
					 | 
				
			||||||
                    IF_VERBOSE(0, verbose_stream() << "Clause is false under assignment: " << cl << "\n");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (clause* cp : s.m_learned) {
 | 
					 | 
				
			||||||
                clause& cl = *cp;
 | 
					 | 
				
			||||||
                bool found = false;
 | 
					 | 
				
			||||||
                for (literal l : cl) {
 | 
					 | 
				
			||||||
                    if (m_assignment[l.var()] != (l.sign() ? l_true : l_false)) {
 | 
					 | 
				
			||||||
                        found = true;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (!found) {
 | 
					 | 
				
			||||||
                    IF_VERBOSE(0, verbose_stream() << "Clause is false under assignment: " << cl << "\n");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            svector<sat::solver::bin_clause> bin;
 | 
					 | 
				
			||||||
            s.collect_bin_clauses(bin, true);
 | 
					 | 
				
			||||||
            for (auto& b : bin) {
 | 
					 | 
				
			||||||
                bool found = false;
 | 
					 | 
				
			||||||
                if (m_assignment[b.first.var()] != (b.first.sign() ? l_true : l_false)) found = true;
 | 
					 | 
				
			||||||
                if (m_assignment[b.second.var()] != (b.second.sign() ? l_true : l_false)) found = true;
 | 
					 | 
				
			||||||
                if (!found) {
 | 
					 | 
				
			||||||
                    IF_VERBOSE(0, verbose_stream() << "Bin clause is false under assignment: " << b.first << " " << b.second << "\n");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            IF_VERBOSE(0, s.display(verbose_stream()));
 | 
					 | 
				
			||||||
            exit(0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,6 +60,11 @@ namespace sat {
 | 
				
			||||||
    class justification;
 | 
					    class justification;
 | 
				
			||||||
    class clause;
 | 
					    class clause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct clause_eh {
 | 
				
			||||||
 | 
					        virtual ~clause_eh() {}
 | 
				
			||||||
 | 
					        virtual void on_clause(unsigned, literal const*, status) = 0;        
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class drat {
 | 
					    class drat {
 | 
				
			||||||
        struct stats {
 | 
					        struct stats {
 | 
				
			||||||
            unsigned m_num_drup = 0;
 | 
					            unsigned m_num_drup = 0;
 | 
				
			||||||
| 
						 | 
					@ -73,6 +78,7 @@ namespace sat {
 | 
				
			||||||
            watched_clause(clause* c, literal l1, literal l2):
 | 
					            watched_clause(clause* c, literal l1, literal l2):
 | 
				
			||||||
                m_clause(c), m_l1(l1), m_l2(l2) {}
 | 
					                m_clause(c), m_l1(l1), m_l2(l2) {}
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					        clause_eh* m_clause_eh = nullptr;
 | 
				
			||||||
        svector<watched_clause>   m_watched_clauses;
 | 
					        svector<watched_clause>   m_watched_clauses;
 | 
				
			||||||
        typedef svector<unsigned> watch;
 | 
					        typedef svector<unsigned> watch;
 | 
				
			||||||
        solver& s;
 | 
					        solver& s;
 | 
				
			||||||
| 
						 | 
					@ -89,9 +95,9 @@ namespace sat {
 | 
				
			||||||
        bool                    m_check_sat = false;
 | 
					        bool                    m_check_sat = false;
 | 
				
			||||||
        bool                    m_check = false;
 | 
					        bool                    m_check = false;
 | 
				
			||||||
        bool                    m_activity = false;
 | 
					        bool                    m_activity = false;
 | 
				
			||||||
        bool                    m_trim = false;
 | 
					 | 
				
			||||||
        stats                   m_stats;
 | 
					        stats                   m_stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void dump_activity();
 | 
					        void dump_activity();
 | 
				
			||||||
        void dump(unsigned n, literal const* c, status st);
 | 
					        void dump(unsigned n, literal const* c, status st);
 | 
				
			||||||
        void bdump(unsigned n, literal const* c, status st);
 | 
					        void bdump(unsigned n, literal const* c, status st);
 | 
				
			||||||
| 
						 | 
					@ -138,17 +144,9 @@ namespace sat {
 | 
				
			||||||
        void add(literal_vector const& c); // add learned clause
 | 
					        void add(literal_vector const& c); // add learned clause
 | 
				
			||||||
        void add(unsigned sz, literal const* lits, status st);
 | 
					        void add(unsigned sz, literal const* lits, status st);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // support for SMT - connect Boolean variables with AST nodes
 | 
					        void set_clause_eh(clause_eh& clause_eh) { m_clause_eh = &clause_eh; }
 | 
				
			||||||
        // associate AST node id with Boolean variable v
 | 
					 | 
				
			||||||
        void bool_def(bool_var v, unsigned n);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // declare AST node n with 'name' and arguments arg
 | 
					        std::ostream* out() { return m_out; }
 | 
				
			||||||
        void def_begin(char id, unsigned n, std::string const& name);
 | 
					 | 
				
			||||||
        void def_add_arg(unsigned arg);
 | 
					 | 
				
			||||||
        void def_end();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // ad-hoc logging until a format is developed
 | 
					 | 
				
			||||||
        void log_adhoc(std::function<void(std::ostream&)>& fn);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool is_cleaned(clause& c) const;        
 | 
					        bool is_cleaned(clause& c) const;        
 | 
				
			||||||
        void del(literal l);
 | 
					        void del(literal l);
 | 
				
			||||||
| 
						 | 
					@ -175,8 +173,6 @@ namespace sat {
 | 
				
			||||||
        svector<std::pair<literal, clause*>> const& units() { return m_units; }
 | 
					        svector<std::pair<literal, clause*>> const& units() { return m_units; }
 | 
				
			||||||
        bool is_drup(unsigned n, literal const* c, literal_vector& units);
 | 
					        bool is_drup(unsigned n, literal const* c, literal_vector& units);
 | 
				
			||||||
        solver& get_solver() { return s; }
 | 
					        solver& get_solver() { return s; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        svector<std::pair<clause&, status>> trim();
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,11 +46,12 @@ def_module_params('sat',
 | 
				
			||||||
                          ('backtrack.conflicts', UINT, 4000, 'number of conflicts before enabling chronological backtracking'),
 | 
					                          ('backtrack.conflicts', UINT, 4000, 'number of conflicts before enabling chronological backtracking'),
 | 
				
			||||||
                          ('threads', UINT, 1, 'number of parallel threads to use'),
 | 
					                          ('threads', UINT, 1, 'number of parallel threads to use'),
 | 
				
			||||||
                          ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'),
 | 
					                          ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'),
 | 
				
			||||||
 | 
					                          ('drat.disable', BOOL, False, 'override anything that enables DRAT'),
 | 
				
			||||||
 | 
					                          ('smt.proof', SYMBOL, '', 'add SMT proof to file'),
 | 
				
			||||||
                          ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'),
 | 
					                          ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'),
 | 
				
			||||||
                          ('drat.binary', BOOL, False, 'use Binary DRAT output format'),
 | 
					                          ('drat.binary', BOOL, False, 'use Binary DRAT output format'),
 | 
				
			||||||
                          ('drat.check_unsat', BOOL, False, 'build up internal proof and check'),
 | 
					                          ('drat.check_unsat', BOOL, False, 'build up internal proof and check'),
 | 
				
			||||||
                          ('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'),
 | 
					                          ('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'),
 | 
				
			||||||
                          ('drup.trim', BOOL, False, 'build and trim drup proof'),
 | 
					 | 
				
			||||||
                          ('drat.activity', BOOL, False, 'dump variable activities'),
 | 
					                          ('drat.activity', BOOL, False, 'dump variable activities'),
 | 
				
			||||||
                          ('cardinality.solver', BOOL, True, 'use cardinality solver'),
 | 
					                          ('cardinality.solver', BOOL, True, 'use cardinality solver'),
 | 
				
			||||||
                          ('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), binary_merge, segmented, solver (use native solver)'),
 | 
					                          ('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), binary_merge, segmented, solver (use native solver)'),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -402,6 +402,7 @@ namespace sat {
 | 
				
			||||||
        extension::scoped_drating _sd(*m_ext.get());
 | 
					        extension::scoped_drating _sd(*m_ext.get());
 | 
				
			||||||
        if (j.get_kind() == justification::EXT_JUSTIFICATION) 
 | 
					        if (j.get_kind() == justification::EXT_JUSTIFICATION) 
 | 
				
			||||||
            fill_ext_antecedents(lit, j, false);
 | 
					            fill_ext_antecedents(lit, j, false);
 | 
				
			||||||
 | 
					        TRACE("sat", tout << "drat-unit\n");
 | 
				
			||||||
        m_drat.add(lit, m_searching);
 | 
					        m_drat.add(lit, m_searching);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -412,6 +413,7 @@ namespace sat {
 | 
				
			||||||
    clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) {
 | 
					    clause * solver::mk_clause_core(unsigned num_lits, literal * lits, sat::status st) {
 | 
				
			||||||
        bool redundant = st.is_redundant();
 | 
					        bool redundant = st.is_redundant();
 | 
				
			||||||
        TRACE("sat", tout << "mk_clause: "  << mk_lits_pp(num_lits, lits) << (redundant?" learned":" aux") << "\n";);
 | 
					        TRACE("sat", tout << "mk_clause: "  << mk_lits_pp(num_lits, lits) << (redundant?" learned":" aux") << "\n";);
 | 
				
			||||||
 | 
					        bool logged = false;
 | 
				
			||||||
        if (!redundant || !st.is_sat()) {
 | 
					        if (!redundant || !st.is_sat()) {
 | 
				
			||||||
            unsigned old_sz = num_lits;
 | 
					            unsigned old_sz = num_lits;
 | 
				
			||||||
            bool keep = simplify_clause(num_lits, lits);
 | 
					            bool keep = simplify_clause(num_lits, lits);
 | 
				
			||||||
| 
						 | 
					@ -420,8 +422,10 @@ namespace sat {
 | 
				
			||||||
                return nullptr; // clause is equivalent to true.
 | 
					                return nullptr; // clause is equivalent to true.
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // if an input clause is simplified, then log the simplified version as learned
 | 
					            // if an input clause is simplified, then log the simplified version as learned
 | 
				
			||||||
            if (m_config.m_drat && old_sz > num_lits)
 | 
					            if (m_config.m_drat && old_sz > num_lits) {
 | 
				
			||||||
                drat_log_clause(num_lits, lits, st);
 | 
					                drat_log_clause(num_lits, lits, st);
 | 
				
			||||||
 | 
					                logged = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ++m_stats.m_non_learned_generation;
 | 
					            ++m_stats.m_non_learned_generation;
 | 
				
			||||||
            if (!m_searching) {
 | 
					            if (!m_searching) {
 | 
				
			||||||
| 
						 | 
					@ -435,7 +439,7 @@ namespace sat {
 | 
				
			||||||
            set_conflict();
 | 
					            set_conflict();
 | 
				
			||||||
            return nullptr;
 | 
					            return nullptr;
 | 
				
			||||||
        case 1:
 | 
					        case 1:
 | 
				
			||||||
            if (m_config.m_drat && (!st.is_sat() || st.is_input()))
 | 
					            if (!logged && m_config.m_drat && (!st.is_sat() || st.is_input()))
 | 
				
			||||||
                drat_log_clause(num_lits, lits, st);
 | 
					                drat_log_clause(num_lits, lits, st);
 | 
				
			||||||
            assign_unit(lits[0]);
 | 
					            assign_unit(lits[0]);
 | 
				
			||||||
            return nullptr;
 | 
					            return nullptr;
 | 
				
			||||||
| 
						 | 
					@ -945,8 +949,7 @@ namespace sat {
 | 
				
			||||||
        if (j.level() == 0) {
 | 
					        if (j.level() == 0) {
 | 
				
			||||||
            if (m_config.m_drat) 
 | 
					            if (m_config.m_drat) 
 | 
				
			||||||
                drat_log_unit(l, j);
 | 
					                drat_log_unit(l, j);
 | 
				
			||||||
            if (!m_config.m_drup_trim)
 | 
					            j = justification(0); // erase justification for level 0
 | 
				
			||||||
                j = justification(0); // erase justification for level 0
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
            VERIFY(!at_base_lvl());
 | 
					            VERIFY(!at_base_lvl());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@ namespace sat {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct no_drat_params : public params_ref {
 | 
					    struct no_drat_params : public params_ref {
 | 
				
			||||||
        no_drat_params() { set_sym("drat.file", symbol()); }        
 | 
					        no_drat_params() { set_bool("drat.disable", true); }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    class solver : public solver_core {
 | 
					    class solver : public solver_core {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,22 +94,9 @@ namespace sat {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    enum class hint_type {
 | 
					    class proof_hint {
 | 
				
			||||||
        null_h,
 | 
					    public:
 | 
				
			||||||
        farkas_h,
 | 
					        virtual ~proof_hint() {}
 | 
				
			||||||
        bound_h,
 | 
					 | 
				
			||||||
        implied_eq_h,    
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct proof_hint {
 | 
					 | 
				
			||||||
        hint_type                              m_ty = hint_type::null_h;
 | 
					 | 
				
			||||||
        vector<std::pair<rational, literal>>   m_literals;
 | 
					 | 
				
			||||||
        vector<std::pair<unsigned, unsigned>>  m_eqs;
 | 
					 | 
				
			||||||
        vector<std::pair<unsigned, unsigned>>  m_diseqs;
 | 
					 | 
				
			||||||
        void reset() { m_ty = hint_type::null_h; m_literals.reset(); m_eqs.reset(); m_diseqs.reset(); }
 | 
					 | 
				
			||||||
        std::string to_string() const;
 | 
					 | 
				
			||||||
        void from_string(char const* s);
 | 
					 | 
				
			||||||
        void from_string(std::string const& s) { from_string(s.c_str()); }
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class status {
 | 
					    class status {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ z3_add_component(sat_smt
 | 
				
			||||||
    euf_invariant.cpp
 | 
					    euf_invariant.cpp
 | 
				
			||||||
    euf_model.cpp
 | 
					    euf_model.cpp
 | 
				
			||||||
    euf_proof.cpp
 | 
					    euf_proof.cpp
 | 
				
			||||||
 | 
					    euf_proof_checker.cpp
 | 
				
			||||||
    euf_relevancy.cpp
 | 
					    euf_relevancy.cpp
 | 
				
			||||||
    euf_solver.cpp
 | 
					    euf_solver.cpp
 | 
				
			||||||
    fpa_solver.cpp
 | 
					    fpa_solver.cpp
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -264,11 +264,12 @@ namespace arith {
 | 
				
			||||||
        SASSERT(k1 != k2 || kind1 != kind2);
 | 
					        SASSERT(k1 != k2 || kind1 != kind2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto bin_clause = [&](sat::literal l1, sat::literal l2) {
 | 
					        auto bin_clause = [&](sat::literal l1, sat::literal l2) {
 | 
				
			||||||
            sat::proof_hint* bound_params = nullptr;
 | 
					            arith_proof_hint* bound_params = nullptr;
 | 
				
			||||||
            if (ctx.use_drat()) {
 | 
					            if (ctx.use_drat()) {
 | 
				
			||||||
                bound_params = &m_farkas2;
 | 
					                m_arith_hint.set_type(ctx, hint_type::farkas_h);
 | 
				
			||||||
                m_farkas2.m_literals[0] = std::make_pair(rational(1), ~l1);
 | 
					                m_arith_hint.add_lit(rational(1), ~l1);
 | 
				
			||||||
                m_farkas2.m_literals[1] = std::make_pair(rational(1), ~l2);
 | 
					                m_arith_hint.add_lit(rational(1), ~l2);
 | 
				
			||||||
 | 
					                bound_params = m_arith_hint.mk(ctx);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            add_clause(l1, l2, bound_params);            
 | 
					            add_clause(l1, l2, bound_params);            
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,8 @@ Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--*/
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include "ast/scoped_proof.h"
 | 
				
			||||||
#include "sat/smt/euf_solver.h"
 | 
					#include "sat/smt/euf_solver.h"
 | 
				
			||||||
#include "sat/smt/arith_solver.h"
 | 
					#include "sat/smt/arith_solver.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,7 +83,6 @@ namespace arith {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void solver::explain_assumptions() {
 | 
					    void solver::explain_assumptions() {
 | 
				
			||||||
        m_arith_hint.reset();
 | 
					 | 
				
			||||||
        unsigned i = 0;
 | 
					        unsigned i = 0;
 | 
				
			||||||
        for (auto const & ev : m_explanation) {
 | 
					        for (auto const & ev : m_explanation) {
 | 
				
			||||||
            ++i;
 | 
					            ++i;
 | 
				
			||||||
| 
						 | 
					@ -91,14 +92,12 @@ namespace arith {
 | 
				
			||||||
            switch (m_constraint_sources[idx]) {
 | 
					            switch (m_constraint_sources[idx]) {
 | 
				
			||||||
            case inequality_source: {
 | 
					            case inequality_source: {
 | 
				
			||||||
                literal lit = m_inequalities[idx];
 | 
					                literal lit = m_inequalities[idx];
 | 
				
			||||||
                m_arith_hint.m_literals.push_back({ev.coeff(), lit});
 | 
					                m_arith_hint.add_lit(ev.coeff(), lit);
 | 
				
			||||||
                break;                
 | 
					                break;                
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case equality_source: {
 | 
					            case equality_source: {
 | 
				
			||||||
                auto [u, v] = m_equalities[idx];
 | 
					                auto [u, v] = m_equalities[idx];
 | 
				
			||||||
                ctx.drat_log_expr(u->get_expr());
 | 
					                m_arith_hint.add_eq(u, v);
 | 
				
			||||||
                ctx.drat_log_expr(v->get_expr());
 | 
					 | 
				
			||||||
                m_arith_hint.m_eqs.push_back({u->get_expr_id(), v->get_expr_id()});
 | 
					 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
| 
						 | 
					@ -115,22 +114,65 @@ namespace arith {
 | 
				
			||||||
     * such that there is a r >= 1
 | 
					     * such that there is a r >= 1
 | 
				
			||||||
     * (r1*a1+..+r_k*a_k) = r*a, (r1*b1+..+r_k*b_k) <= r*b
 | 
					     * (r1*a1+..+r_k*a_k) = r*a, (r1*b1+..+r_k*b_k) <= r*b
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    sat::proof_hint const* solver::explain(sat::hint_type ty, sat::literal lit) {
 | 
					    arith_proof_hint const* solver::explain(hint_type ty, sat::literal lit) {
 | 
				
			||||||
        if (!ctx.use_drat())
 | 
					        if (!ctx.use_drat())
 | 
				
			||||||
            return nullptr;
 | 
					            return nullptr;
 | 
				
			||||||
        m_arith_hint.m_ty = ty;
 | 
					        m_arith_hint.set_type(ctx, ty);
 | 
				
			||||||
        explain_assumptions();
 | 
					        explain_assumptions();
 | 
				
			||||||
        if (lit != sat::null_literal)
 | 
					        if (lit != sat::null_literal)
 | 
				
			||||||
            m_arith_hint.m_literals.push_back({rational(1), ~lit});
 | 
					            m_arith_hint.add_lit(rational(1), ~lit);
 | 
				
			||||||
        return &m_arith_hint;
 | 
					        return m_arith_hint.mk(ctx);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sat::proof_hint const* solver::explain_implied_eq(euf::enode* a, euf::enode* b) {
 | 
					    arith_proof_hint const* solver::explain_implied_eq(euf::enode* a, euf::enode* b) {
 | 
				
			||||||
        if (!ctx.use_drat())
 | 
					        if (!ctx.use_drat())
 | 
				
			||||||
            return nullptr;
 | 
					            return nullptr;
 | 
				
			||||||
        m_arith_hint.m_ty = sat::hint_type::implied_eq_h;
 | 
					        m_arith_hint.set_type(ctx, hint_type::implied_eq_h);
 | 
				
			||||||
        explain_assumptions();
 | 
					        explain_assumptions();
 | 
				
			||||||
        m_arith_hint.m_diseqs.push_back({a->get_expr_id(), b->get_expr_id()});
 | 
					        m_arith_hint.add_diseq(a, b);
 | 
				
			||||||
        return &m_arith_hint;
 | 
					        return m_arith_hint.mk(ctx);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr* arith_proof_hint::get_hint(euf::solver& s) const {
 | 
				
			||||||
 | 
					        ast_manager& m = s.get_manager();
 | 
				
			||||||
 | 
					        family_id fid = m.get_family_id("arith");
 | 
				
			||||||
 | 
					        arith_util arith(m);
 | 
				
			||||||
 | 
					        solver& a = dynamic_cast<solver&>(*s.fid2solver(fid));
 | 
				
			||||||
 | 
					        char const* name;
 | 
				
			||||||
 | 
					        switch (m_ty) {
 | 
				
			||||||
 | 
					        case hint_type::farkas_h:
 | 
				
			||||||
 | 
					            name = "farkas";
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case hint_type::bound_h:
 | 
				
			||||||
 | 
					            name = "bound";
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case hint_type::implied_eq_h:
 | 
				
			||||||
 | 
					            name = "implied-eq";
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        rational lc(1);
 | 
				
			||||||
 | 
					        for (unsigned i = m_lit_head; i < m_lit_tail; ++i) 
 | 
				
			||||||
 | 
					            lc = lcm(lc, denominator(a.m_arith_hint.lit(i).first));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        expr_ref_vector args(m);
 | 
				
			||||||
 | 
					        sort_ref_vector sorts(m);
 | 
				
			||||||
 | 
					        for (unsigned i = m_lit_head; i < m_lit_tail; ++i) {            
 | 
				
			||||||
 | 
					            auto const& [coeff, lit] = a.m_arith_hint.lit(i);
 | 
				
			||||||
 | 
					            args.push_back(arith.mk_int(abs(coeff*lc)));
 | 
				
			||||||
 | 
					            args.push_back(s.literal2expr(lit));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (unsigned i = m_eq_head; i < m_eq_tail; ++i) {
 | 
				
			||||||
 | 
					            auto const& [x, y, is_eq] = a.m_arith_hint.eq(i);            
 | 
				
			||||||
 | 
					            expr_ref eq(m.mk_eq(x->get_expr(), y->get_expr()), m);
 | 
				
			||||||
 | 
					            if (!is_eq) eq = m.mk_not(eq);
 | 
				
			||||||
 | 
					            args.push_back(arith.mk_int(1));
 | 
				
			||||||
 | 
					            args.push_back(eq);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (expr* arg : args)
 | 
				
			||||||
 | 
					            sorts.push_back(arg->get_sort());
 | 
				
			||||||
 | 
					        sort* range = m.mk_proof_sort();
 | 
				
			||||||
 | 
					        func_decl* d = m.mk_func_decl(symbol(name), args.size(), sorts.data(), range);
 | 
				
			||||||
 | 
					        expr* r = m.mk_app(d, args);
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
/*++
 | 
					/*++
 | 
				
			||||||
Copyright (c) 2020 Microsoft Corporation
 | 
					Copyright (c) 2022 Microsoft Corporation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Module Name:
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,15 @@ Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Author:
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Nikolaj Bjorner (nbjorner) 2020-09-08
 | 
					    Nikolaj Bjorner (nbjorner) 2022-08-28
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Notes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The module assumes a limited repertoire of arithmetic proof rules.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- farkas - inequalities, equalities and disequalities with coefficients
 | 
				
			||||||
 | 
					- implied-eq - last literal is a disequality. The literals before imply the corresponding equality.
 | 
				
			||||||
 | 
					- bound - last literal is a bound. It is implied by prior literals.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--*/
 | 
					--*/
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
| 
						 | 
					@ -19,11 +27,12 @@ Author:
 | 
				
			||||||
#include "util/obj_pair_set.h"
 | 
					#include "util/obj_pair_set.h"
 | 
				
			||||||
#include "ast/ast_trail.h"
 | 
					#include "ast/ast_trail.h"
 | 
				
			||||||
#include "ast/arith_decl_plugin.h"
 | 
					#include "ast/arith_decl_plugin.h"
 | 
				
			||||||
 | 
					#include "sat/smt/euf_proof_checker.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace arith {
 | 
					namespace arith {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class proof_checker {
 | 
					    class proof_checker : public euf::proof_checker_plugin {
 | 
				
			||||||
        struct row {
 | 
					        struct row {
 | 
				
			||||||
            obj_map<expr, rational> m_coeffs;
 | 
					            obj_map<expr, rational> m_coeffs;
 | 
				
			||||||
            rational m_coeff;
 | 
					            rational m_coeff;
 | 
				
			||||||
| 
						 | 
					@ -131,11 +140,13 @@ namespace arith {
 | 
				
			||||||
            SASSERT(m_todo.empty());
 | 
					            SASSERT(m_todo.empty());
 | 
				
			||||||
            m_todo.push_back({ mul, e });
 | 
					            m_todo.push_back({ mul, e });
 | 
				
			||||||
            rational coeff1;
 | 
					            rational coeff1;
 | 
				
			||||||
            expr* e1, *e2;
 | 
					            expr* e1, *e2, *e3;
 | 
				
			||||||
            for (unsigned i = 0; i < m_todo.size(); ++i) {
 | 
					            for (unsigned i = 0; i < m_todo.size(); ++i) {
 | 
				
			||||||
                auto [coeff, e] = m_todo[i];
 | 
					                auto [coeff, e] = m_todo[i];
 | 
				
			||||||
                if (a.is_mul(e, e1, e2) && a.is_numeral(e1, coeff1))
 | 
					                if (a.is_mul(e, e1, e2) && a.is_numeral(e1, coeff1))
 | 
				
			||||||
                    m_todo.push_back({coeff*coeff1, e2});
 | 
					                    m_todo.push_back({coeff*coeff1, e2});
 | 
				
			||||||
 | 
					                else if (a.is_mul(e, e1, e2) && a.is_uminus(e1, e3) && a.is_numeral(e3, coeff1))
 | 
				
			||||||
 | 
					                    m_todo.push_back({-coeff*coeff1, e2});
 | 
				
			||||||
                else if (a.is_mul(e, e1, e2) && a.is_numeral(e2, coeff1))
 | 
					                else if (a.is_mul(e, e1, e2) && a.is_numeral(e2, coeff1))
 | 
				
			||||||
                    m_todo.push_back({coeff*coeff1, e1});
 | 
					                    m_todo.push_back({coeff*coeff1, e1});
 | 
				
			||||||
                else if (a.is_add(e))
 | 
					                else if (a.is_add(e))
 | 
				
			||||||
| 
						 | 
					@ -149,6 +160,8 @@ namespace arith {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                else if (a.is_numeral(e, coeff1)) 
 | 
					                else if (a.is_numeral(e, coeff1)) 
 | 
				
			||||||
                    r.m_coeff += coeff*coeff1;
 | 
					                    r.m_coeff += coeff*coeff1;
 | 
				
			||||||
 | 
					                else if (a.is_uminus(e, e1) && a.is_numeral(e1, coeff1))
 | 
				
			||||||
 | 
					                    r.m_coeff -= coeff*coeff1;
 | 
				
			||||||
                else
 | 
					                else
 | 
				
			||||||
                    add(r, e, coeff);
 | 
					                    add(r, e, coeff);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -300,6 +313,8 @@ namespace arith {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    public:
 | 
					    public:
 | 
				
			||||||
        proof_checker(ast_manager& m): m(m), a(m) {}
 | 
					        proof_checker(ast_manager& m): m(m), a(m) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ~proof_checker() override {}
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        void reset() {
 | 
					        void reset() {
 | 
				
			||||||
            m_ineq.reset();
 | 
					            m_ineq.reset();
 | 
				
			||||||
| 
						 | 
					@ -350,6 +365,70 @@ namespace arith {
 | 
				
			||||||
            return out;
 | 
					            return out;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool check(expr_ref_vector const& clause, app* jst, expr_ref_vector& units) override {
 | 
				
			||||||
 | 
					            reset();
 | 
				
			||||||
 | 
					            expr_mark pos, neg;
 | 
				
			||||||
 | 
					            for (expr* e : clause)
 | 
				
			||||||
 | 
					                if (m.is_not(e, e))
 | 
				
			||||||
 | 
					                    neg.mark(e, true);
 | 
				
			||||||
 | 
					                else
 | 
				
			||||||
 | 
					                    pos.mark(e, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (jst->get_name() == symbol("farkas")) {
 | 
				
			||||||
 | 
					                bool even = true;
 | 
				
			||||||
 | 
					                rational coeff;
 | 
				
			||||||
 | 
					                expr* x, *y;
 | 
				
			||||||
 | 
					                for (expr* arg : *jst) {
 | 
				
			||||||
 | 
					                    if (even) {
 | 
				
			||||||
 | 
					                        if (!a.is_numeral(arg, coeff)) {
 | 
				
			||||||
 | 
					                            IF_VERBOSE(0, verbose_stream() << "not numeral " << mk_pp(jst, m) << "\n");
 | 
				
			||||||
 | 
					                            return false;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        bool sign = m.is_not(arg, arg);
 | 
				
			||||||
 | 
					                        if (a.is_le(arg) || a.is_lt(arg) || a.is_ge(arg) || a.is_gt(arg)) 
 | 
				
			||||||
 | 
					                            add_ineq(coeff, arg, sign);
 | 
				
			||||||
 | 
					                        else if (m.is_eq(arg, x, y)) {
 | 
				
			||||||
 | 
					                            if (sign)
 | 
				
			||||||
 | 
					                                add_diseq(x, y);
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                                add_eq(x, y);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                            return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (sign && !pos.is_marked(arg)) {
 | 
				
			||||||
 | 
					                            units.push_back(m.mk_not(arg));
 | 
				
			||||||
 | 
					                            pos.mark(arg, false);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (!sign && !neg.is_marked(arg)) {
 | 
				
			||||||
 | 
					                            units.push_back(arg);
 | 
				
			||||||
 | 
					                            neg.mark(arg, false);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                    }                        
 | 
				
			||||||
 | 
					                    even = !even;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (check_farkas()) {
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                IF_VERBOSE(0, verbose_stream() << "did not check farkas\n" << mk_pp(jst, m) << "\n"; display(verbose_stream()); );
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // todo: rules for bounds and implied-by
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            IF_VERBOSE(0, verbose_stream() << "did not check " << mk_pp(jst, m) << "\n");
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void register_plugins(euf::proof_checker& pc) override {
 | 
				
			||||||
 | 
					            pc.register_plugin(symbol("farkas"), this);
 | 
				
			||||||
 | 
					            pc.register_plugin(symbol("bound"), this);
 | 
				
			||||||
 | 
					            pc.register_plugin(symbol("implied-eq"), this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,8 +39,6 @@ namespace arith {
 | 
				
			||||||
        lp().settings().set_random_seed(get_config().m_random_seed);
 | 
					        lp().settings().set_random_seed(get_config().m_random_seed);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        m_lia = alloc(lp::int_solver, *m_solver.get());
 | 
					        m_lia = alloc(lp::int_solver, *m_solver.get());
 | 
				
			||||||
        m_farkas2.m_ty = sat::hint_type::farkas_h;
 | 
					 | 
				
			||||||
        m_farkas2.m_literals.resize(2);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    solver::~solver() {
 | 
					    solver::~solver() {
 | 
				
			||||||
| 
						 | 
					@ -197,11 +195,12 @@ namespace arith {
 | 
				
			||||||
        reset_evidence();
 | 
					        reset_evidence();
 | 
				
			||||||
        m_core.push_back(lit1);
 | 
					        m_core.push_back(lit1);
 | 
				
			||||||
        TRACE("arith", tout << lit2 << " <- " << m_core << "\n";);
 | 
					        TRACE("arith", tout << lit2 << " <- " << m_core << "\n";);
 | 
				
			||||||
        sat::proof_hint* ph = nullptr;
 | 
					        arith_proof_hint* ph = nullptr;
 | 
				
			||||||
        if (ctx.use_drat()) {
 | 
					        if (ctx.use_drat()) {
 | 
				
			||||||
            ph = &m_farkas2;
 | 
					            m_arith_hint.set_type(ctx, hint_type::farkas_h);
 | 
				
			||||||
            m_farkas2.m_literals[0] = std::make_pair(rational(1), lit1);
 | 
					            m_arith_hint.add_lit(rational(1), lit1);
 | 
				
			||||||
            m_farkas2.m_literals[1] = std::make_pair(rational(1), ~lit2);
 | 
					            m_arith_hint.add_lit(rational(1), ~lit2);
 | 
				
			||||||
 | 
					            ph = m_arith_hint.mk(ctx);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        assign(lit2, m_core, m_eqs, ph); 
 | 
					        assign(lit2, m_core, m_eqs, ph); 
 | 
				
			||||||
        ++m_stats.m_bounds_propagations;
 | 
					        ++m_stats.m_bounds_propagations;
 | 
				
			||||||
| 
						 | 
					@ -262,7 +261,7 @@ namespace arith {
 | 
				
			||||||
            TRACE("arith", for (auto lit : m_core) tout << lit << ": " << s().value(lit) << "\n";);
 | 
					            TRACE("arith", for (auto lit : m_core) tout << lit << ": " << s().value(lit) << "\n";);
 | 
				
			||||||
            DEBUG_CODE(for (auto lit : m_core) { VERIFY(s().value(lit) == l_true); });
 | 
					            DEBUG_CODE(for (auto lit : m_core) { VERIFY(s().value(lit) == l_true); });
 | 
				
			||||||
            ++m_stats.m_bound_propagations1;
 | 
					            ++m_stats.m_bound_propagations1;
 | 
				
			||||||
            assign(lit, m_core, m_eqs, explain(sat::hint_type::bound_h, lit));
 | 
					            assign(lit, m_core, m_eqs, explain(hint_type::bound_h, lit));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (should_refine_bounds() && first)
 | 
					        if (should_refine_bounds() && first)
 | 
				
			||||||
| 
						 | 
					@ -378,7 +377,7 @@ namespace arith {
 | 
				
			||||||
        reset_evidence();
 | 
					        reset_evidence();
 | 
				
			||||||
        m_explanation.clear();
 | 
					        m_explanation.clear();
 | 
				
			||||||
        lp().explain_implied_bound(be, m_bp);
 | 
					        lp().explain_implied_bound(be, m_bp);
 | 
				
			||||||
        assign(bound, m_core, m_eqs, explain(sat::hint_type::farkas_h, bound));
 | 
					        assign(bound, m_core, m_eqs, explain(hint_type::farkas_h, bound));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1178,7 +1177,7 @@ namespace arith {
 | 
				
			||||||
            app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper());
 | 
					            app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper());
 | 
				
			||||||
            IF_VERBOSE(4, verbose_stream() << "cut " << b << "\n");
 | 
					            IF_VERBOSE(4, verbose_stream() << "cut " << b << "\n");
 | 
				
			||||||
            literal lit = expr2literal(b);
 | 
					            literal lit = expr2literal(b);
 | 
				
			||||||
            assign(lit, m_core, m_eqs, explain(sat::hint_type::bound_h, lit));
 | 
					            assign(lit, m_core, m_eqs, explain(hint_type::bound_h, lit));
 | 
				
			||||||
            lia_check = l_false;
 | 
					            lia_check = l_false;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -1200,7 +1199,7 @@ namespace arith {
 | 
				
			||||||
        return lia_check;
 | 
					        return lia_check;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void solver::assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, sat::proof_hint const* pma) {        
 | 
					    void solver::assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, euf::th_proof_hint const* pma) {        
 | 
				
			||||||
        if (core.size() < small_lemma_size() && eqs.empty()) {
 | 
					        if (core.size() < small_lemma_size() && eqs.empty()) {
 | 
				
			||||||
            m_core2.reset();
 | 
					            m_core2.reset();
 | 
				
			||||||
            for (auto const& c : core)
 | 
					            for (auto const& c : core)
 | 
				
			||||||
| 
						 | 
					@ -1247,7 +1246,7 @@ namespace arith {
 | 
				
			||||||
        for (literal& c : m_core)
 | 
					        for (literal& c : m_core)
 | 
				
			||||||
            c.neg();
 | 
					            c.neg();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        add_clause(m_core, explain(sat::hint_type::farkas_h));
 | 
					        add_clause(m_core, explain(hint_type::farkas_h));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool solver::is_infeasible() const {
 | 
					    bool solver::is_infeasible() const {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,8 +48,61 @@ namespace arith {
 | 
				
			||||||
    typedef sat::literal_vector literal_vector;
 | 
					    typedef sat::literal_vector literal_vector;
 | 
				
			||||||
    typedef lp_api::bound<sat::literal> api_bound;
 | 
					    typedef lp_api::bound<sat::literal> api_bound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class hint_type {
 | 
				
			||||||
 | 
					        farkas_h,
 | 
				
			||||||
 | 
					        bound_h,
 | 
				
			||||||
 | 
					        implied_eq_h    
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct arith_proof_hint : public euf::th_proof_hint {
 | 
				
			||||||
 | 
					        hint_type                              m_ty;
 | 
				
			||||||
 | 
					        unsigned m_lit_head, m_lit_tail, m_eq_head, m_eq_tail;
 | 
				
			||||||
 | 
					        arith_proof_hint(hint_type t, unsigned lh, unsigned lt, unsigned eh, unsigned et):
 | 
				
			||||||
 | 
					            m_ty(t), m_lit_head(lh), m_lit_tail(lt), m_eq_head(eh), m_eq_tail(et) {}
 | 
				
			||||||
 | 
					        expr* get_hint(euf::solver& s) const override;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class arith_proof_hint_builder {
 | 
				
			||||||
 | 
					        vector<std::pair<rational, literal>>   m_literals;
 | 
				
			||||||
 | 
					        svector<std::tuple<euf::enode*,euf::enode*,bool>> m_eqs;
 | 
				
			||||||
 | 
					        hint_type                              m_ty;
 | 
				
			||||||
 | 
					        unsigned                               m_lit_head = 0, m_lit_tail = 0, m_eq_head = 0, m_eq_tail;
 | 
				
			||||||
 | 
					        void reset() { m_lit_head = m_lit_tail; m_eq_head = m_eq_tail; }
 | 
				
			||||||
 | 
					        void add(euf::enode* a, euf::enode* b, bool is_eq) {
 | 
				
			||||||
 | 
					            if (m_eq_tail < m_eqs.size()) 
 | 
				
			||||||
 | 
					                m_eqs[m_eq_tail] = std::tuple(a, b, is_eq); 
 | 
				
			||||||
 | 
					            else 
 | 
				
			||||||
 | 
					                m_eqs.push_back(std::tuple(a, b, is_eq)); 
 | 
				
			||||||
 | 
					            m_eq_tail++; 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        void set_type(euf::solver& ctx, hint_type ty) { 
 | 
				
			||||||
 | 
					            ctx.push(value_trail<unsigned>(m_eq_tail)); 
 | 
				
			||||||
 | 
					            ctx.push(value_trail<unsigned>(m_lit_tail)); 
 | 
				
			||||||
 | 
					            m_ty = ty; 
 | 
				
			||||||
 | 
					            reset(); 
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); }
 | 
				
			||||||
 | 
					        void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); }
 | 
				
			||||||
 | 
					        void add_lit(rational const& coeff, literal lit) { 
 | 
				
			||||||
 | 
					            if (m_lit_tail < m_literals.size())
 | 
				
			||||||
 | 
					                m_literals[m_lit_tail] = {coeff, lit};
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                m_literals.push_back({coeff, lit}); 
 | 
				
			||||||
 | 
					            m_lit_tail++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        std::pair<rational, literal> const& lit(unsigned i) const { return m_literals[i]; }
 | 
				
			||||||
 | 
					        std::tuple<enode*, enode*, bool> const& eq(unsigned i) const { return m_eqs[i]; }
 | 
				
			||||||
 | 
					        arith_proof_hint* mk(euf::solver& s) { 
 | 
				
			||||||
 | 
					            return new (s.get_region()) arith_proof_hint(m_ty, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class solver : public euf::th_euf_solver {
 | 
					    class solver : public euf::th_euf_solver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        friend struct arith_proof_hint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        struct scope {
 | 
					        struct scope {
 | 
				
			||||||
            unsigned m_bounds_lim;
 | 
					            unsigned m_bounds_lim;
 | 
				
			||||||
            unsigned m_idiv_lim;
 | 
					            unsigned m_idiv_lim;
 | 
				
			||||||
| 
						 | 
					@ -414,15 +467,15 @@ namespace arith {
 | 
				
			||||||
        void set_conflict();
 | 
					        void set_conflict();
 | 
				
			||||||
        void set_conflict_or_lemma(literal_vector const& core, bool is_conflict);
 | 
					        void set_conflict_or_lemma(literal_vector const& core, bool is_conflict);
 | 
				
			||||||
        void set_evidence(lp::constraint_index idx);
 | 
					        void set_evidence(lp::constraint_index idx);
 | 
				
			||||||
        void assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, sat::proof_hint const* pma);
 | 
					        void assign(literal lit, literal_vector const& core, svector<enode_pair> const& eqs, euf::th_proof_hint const* pma);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        void false_case_of_check_nla(const nla::lemma& l);        
 | 
					        void false_case_of_check_nla(const nla::lemma& l);        
 | 
				
			||||||
        void dbg_finalize_model(model& mdl);
 | 
					        void dbg_finalize_model(model& mdl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sat::proof_hint m_arith_hint;
 | 
					        arith_proof_hint_builder m_arith_hint;
 | 
				
			||||||
        sat::proof_hint m_farkas2;
 | 
					
 | 
				
			||||||
        sat::proof_hint const* explain(sat::hint_type ty, sat::literal lit = sat::null_literal);
 | 
					        arith_proof_hint const* explain(hint_type ty, sat::literal lit = sat::null_literal);
 | 
				
			||||||
        sat::proof_hint const* explain_implied_eq(euf::enode* a, euf::enode* b);
 | 
					        arith_proof_hint const* explain_implied_eq(euf::enode* a, euf::enode* b);
 | 
				
			||||||
        void explain_assumptions();
 | 
					        void explain_assumptions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,16 +49,19 @@ namespace bv {
 | 
				
			||||||
        update_glue(*other);
 | 
					        update_glue(*other);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        vv::push_to_front(m_queue, other);
 | 
					        vv::push_to_front(m_queue, other);
 | 
				
			||||||
        if (other == n) {
 | 
					        bool do_gc = other == n;
 | 
				
			||||||
 | 
					        if (other == n) 
 | 
				
			||||||
            new_tmp();        
 | 
					            new_tmp();        
 | 
				
			||||||
            gc();
 | 
					               
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (other->m_glue == 0) {
 | 
					        if (other->m_glue == 0) {
 | 
				
			||||||
 | 
					            do_gc = false;
 | 
				
			||||||
            remove(other);
 | 
					            remove(other);
 | 
				
			||||||
            add_cc(v1, v2);
 | 
					            add_cc(v1, v2);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else if (other->m_count > 2*m_propagate_high_watermark) 
 | 
					        else if (other->m_count > 2*m_propagate_high_watermark) 
 | 
				
			||||||
            propagate();
 | 
					            propagate();
 | 
				
			||||||
 | 
					        if (do_gc)
 | 
				
			||||||
 | 
					            gc();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void ackerman::used_diseq_eh(euf::theory_var v1, euf::theory_var v2) {
 | 
					    void ackerman::used_diseq_eh(euf::theory_var v1, euf::theory_var v2) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -429,6 +429,8 @@ namespace bv {
 | 
				
			||||||
            args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));        
 | 
					            args.push_back(m.mk_ite(b, m_autil.mk_int(power2(i++)), zero));        
 | 
				
			||||||
        expr_ref sum(m_autil.mk_add(sz, args.data()), m);
 | 
					        expr_ref sum(m_autil.mk_add(sz, args.data()), m);
 | 
				
			||||||
        sat::literal lit = eq_internalize(n, sum);
 | 
					        sat::literal lit = eq_internalize(n, sum);
 | 
				
			||||||
 | 
					        m_bv2ints.push_back(expr2enode(n));
 | 
				
			||||||
 | 
					        ctx.push(push_back_vector<euf::enode_vector>(m_bv2ints));
 | 
				
			||||||
	add_unit(lit);
 | 
						add_unit(lit);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -211,9 +211,8 @@ namespace bv {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        euf::enode* n1 = var2enode(eq.v1());
 | 
					        euf::enode* n1 = var2enode(eq.v1());
 | 
				
			||||||
        for (euf::enode* bv2int : euf::enode_class(n1)) {
 | 
					
 | 
				
			||||||
            if (!bv.is_bv2int(bv2int->get_expr()))
 | 
					        auto propagate_bv2int = [&](euf::enode* bv2int) {
 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            euf::enode* bv2int_arg = bv2int->get_arg(0);
 | 
					            euf::enode* bv2int_arg = bv2int->get_arg(0);
 | 
				
			||||||
            for (euf::enode* p : euf::enode_parents(n1->get_root())) {
 | 
					            for (euf::enode* p : euf::enode_parents(n1->get_root())) {
 | 
				
			||||||
                if (bv.is_int2bv(p->get_expr()) && p->get_sort() == bv2int_arg->get_sort() && p->get_root() != bv2int_arg->get_root()) {
 | 
					                if (bv.is_int2bv(p->get_expr()) && p->get_sort() == bv2int_arg->get_sort() && p->get_root() != bv2int_arg->get_root()) {
 | 
				
			||||||
| 
						 | 
					@ -224,6 +223,19 @@ namespace bv {
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (m_bv2ints.size() < n1->class_size()) {
 | 
				
			||||||
 | 
					            for (auto* bv2int : m_bv2ints) {
 | 
				
			||||||
 | 
					                if (bv2int->get_root() == n1->get_root())
 | 
				
			||||||
 | 
					                    propagate_bv2int(bv2int);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            for (euf::enode* bv2int : euf::enode_class(n1)) {                
 | 
				
			||||||
 | 
					                if (bv.is_bv2int(bv2int->get_expr()))
 | 
				
			||||||
 | 
					                    propagate_bv2int(bv2int);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -279,6 +291,8 @@ namespace bv {
 | 
				
			||||||
            ++m_stats.m_num_ne2bit;
 | 
					            ++m_stats.m_num_ne2bit;
 | 
				
			||||||
            s().assign(consequent, mk_ne2bit_justification(undef_idx, v1, v2, consequent, antecedent));
 | 
					            s().assign(consequent, mk_ne2bit_justification(undef_idx, v1, v2, consequent, antecedent));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        else if (!get_config().m_bv_eq_axioms) 
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
        else if (s().at_search_lvl()) {
 | 
					        else if (s().at_search_lvl()) {
 | 
				
			||||||
            force_push();
 | 
					            force_push();
 | 
				
			||||||
            assert_ackerman(v1, v2);
 | 
					            assert_ackerman(v1, v2);
 | 
				
			||||||
| 
						 | 
					@ -377,8 +391,8 @@ namespace bv {
 | 
				
			||||||
        if (c.m_kind != bv_justification::kind_t::bit2ne) {
 | 
					        if (c.m_kind != bv_justification::kind_t::bit2ne) {
 | 
				
			||||||
            expr* e1 = var2expr(c.m_v1);
 | 
					            expr* e1 = var2expr(c.m_v1);
 | 
				
			||||||
            expr* e2 = var2expr(c.m_v2);
 | 
					            expr* e2 = var2expr(c.m_v2);
 | 
				
			||||||
            eq = m.mk_eq(e1, e2);       
 | 
					            eq = m.mk_eq(e1, e2);      
 | 
				
			||||||
            ctx.drat_eq_def(leq, eq);
 | 
					            ctx.set_tmp_bool_var(leq.var(), eq);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sat::literal_vector lits;
 | 
					        sat::literal_vector lits;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,8 +207,9 @@ namespace bv {
 | 
				
			||||||
        literal_vector             m_tmp_literals;
 | 
					        literal_vector             m_tmp_literals;
 | 
				
			||||||
        svector<propagation_item>  m_prop_queue;
 | 
					        svector<propagation_item>  m_prop_queue;
 | 
				
			||||||
        unsigned_vector            m_prop_queue_lim;
 | 
					        unsigned_vector            m_prop_queue_lim;
 | 
				
			||||||
        unsigned                   m_prop_queue_head { 0 };
 | 
					        unsigned                   m_prop_queue_head = 0;
 | 
				
			||||||
        sat::literal               m_true { sat::null_literal };
 | 
					        sat::literal               m_true = sat::null_literal;
 | 
				
			||||||
 | 
					        euf::enode_vector          m_bv2ints;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // internalize
 | 
					        // internalize
 | 
				
			||||||
        void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); }
 | 
					        void insert_bv2a(bool_var bv, atom * a) { m_bool_var2atom.setx(bv, a, 0); }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,107 +16,26 @@ Author:
 | 
				
			||||||
--*/
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "sat/smt/euf_solver.h"
 | 
					#include "sat/smt/euf_solver.h"
 | 
				
			||||||
 | 
					#include "ast/ast_util.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace euf {
 | 
					namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void solver::init_drat() {
 | 
					    void solver::init_proof() {
 | 
				
			||||||
        if (!m_drat_initialized) {
 | 
					        if (!m_proof_initialized) {
 | 
				
			||||||
            get_drat().add_theory(get_id(), symbol("euf"));
 | 
					            get_drat().add_theory(get_id(), symbol("euf"));
 | 
				
			||||||
            get_drat().add_theory(m.get_basic_family_id(), symbol("bool"));
 | 
					            get_drat().add_theory(m.get_basic_family_id(), symbol("bool"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        m_drat_initialized = true;
 | 
					        if (!m_proof_out && s().get_config().m_drat && 
 | 
				
			||||||
    }
 | 
					            (get_config().m_lemmas2console || s().get_config().m_smt_proof.is_non_empty_string())) {
 | 
				
			||||||
 | 
					            TRACE("euf", tout << "init-proof\n");
 | 
				
			||||||
    void solver::drat_log_params(func_decl* f) {
 | 
					            m_proof_out = alloc(std::ofstream, s().get_config().m_smt_proof.str(), std::ios_base::out);
 | 
				
			||||||
        for (unsigned i = f->get_num_parameters(); i-- > 0; ) {
 | 
					            if (get_config().m_lemmas2console) 
 | 
				
			||||||
            auto const& p = f->get_parameter(i);
 | 
					                get_drat().set_clause_eh(*this);
 | 
				
			||||||
            if (!p.is_ast()) 
 | 
					            if (s().get_config().m_smt_proof.is_non_empty_string()) 
 | 
				
			||||||
                continue;
 | 
					                get_drat().set_clause_eh(*this);
 | 
				
			||||||
            ast* a = p.get_ast();
 | 
					 | 
				
			||||||
            if (is_func_decl(a))
 | 
					 | 
				
			||||||
                drat_log_decl(to_func_decl(a));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					        m_proof_initialized = true;
 | 
				
			||||||
    void solver::drat_log_expr1(expr* e) {
 | 
					 | 
				
			||||||
        if (is_app(e)) {
 | 
					 | 
				
			||||||
            app* a = to_app(e);
 | 
					 | 
				
			||||||
            drat_log_params(a->get_decl());
 | 
					 | 
				
			||||||
            drat_log_decl(a->get_decl());
 | 
					 | 
				
			||||||
            std::stringstream strm;
 | 
					 | 
				
			||||||
            strm << mk_ismt2_func(a->get_decl(), m);
 | 
					 | 
				
			||||||
            get_drat().def_begin('e', e->get_id(), strm.str());
 | 
					 | 
				
			||||||
            for (expr* arg : *a)
 | 
					 | 
				
			||||||
                get_drat().def_add_arg(arg->get_id());
 | 
					 | 
				
			||||||
            get_drat().def_end();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (is_var(e)) {
 | 
					 | 
				
			||||||
            var* v = to_var(e);
 | 
					 | 
				
			||||||
            get_drat().def_begin('v', v->get_id(), "" + mk_pp(e->get_sort(), m));
 | 
					 | 
				
			||||||
            get_drat().def_add_arg(v->get_idx());
 | 
					 | 
				
			||||||
            get_drat().def_end();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else if (is_quantifier(e)) {
 | 
					 | 
				
			||||||
            quantifier* q = to_quantifier(e);
 | 
					 | 
				
			||||||
            std::stringstream strm;           
 | 
					 | 
				
			||||||
            strm << "(" << (is_forall(q) ? "forall" : (is_exists(q) ? "exists" : "lambda"));
 | 
					 | 
				
			||||||
            for (unsigned i = 0; i < q->get_num_decls(); ++i) 
 | 
					 | 
				
			||||||
                strm << " (" << q->get_decl_name(i) << " " << mk_pp(q->get_decl_sort(i), m) << ")";            
 | 
					 | 
				
			||||||
            strm << ")";
 | 
					 | 
				
			||||||
            get_drat().def_begin('q', q->get_id(), strm.str());
 | 
					 | 
				
			||||||
            get_drat().def_add_arg(q->get_expr()->get_id());
 | 
					 | 
				
			||||||
            get_drat().def_end();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else 
 | 
					 | 
				
			||||||
            UNREACHABLE();
 | 
					 | 
				
			||||||
        m_drat_asts.insert(e);
 | 
					 | 
				
			||||||
        push(insert_obj_trail<ast>(m_drat_asts, e));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void solver::drat_log_expr(expr* e) {
 | 
					 | 
				
			||||||
        if (m_drat_asts.contains(e))
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        ptr_vector<expr>::scoped_stack _sc(m_drat_todo);
 | 
					 | 
				
			||||||
        m_drat_todo.push_back(e);
 | 
					 | 
				
			||||||
        while (!m_drat_todo.empty()) {
 | 
					 | 
				
			||||||
            e = m_drat_todo.back();
 | 
					 | 
				
			||||||
            unsigned sz = m_drat_todo.size();
 | 
					 | 
				
			||||||
            if (is_app(e)) 
 | 
					 | 
				
			||||||
                for (expr* arg : *to_app(e))
 | 
					 | 
				
			||||||
                    if (!m_drat_asts.contains(arg))
 | 
					 | 
				
			||||||
                        m_drat_todo.push_back(arg);
 | 
					 | 
				
			||||||
            if (is_quantifier(e)) {
 | 
					 | 
				
			||||||
                expr* arg = to_quantifier(e)->get_expr();
 | 
					 | 
				
			||||||
                if (!m_drat_asts.contains(arg))
 | 
					 | 
				
			||||||
                    m_drat_todo.push_back(arg);                
 | 
					 | 
				
			||||||
            }                
 | 
					 | 
				
			||||||
            if (m_drat_todo.size() != sz)
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            if (!m_drat_asts.contains(e))
 | 
					 | 
				
			||||||
                drat_log_expr1(e);
 | 
					 | 
				
			||||||
            m_drat_todo.pop_back();                   
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void solver::drat_bool_def(sat::bool_var v, expr* e) {
 | 
					 | 
				
			||||||
        if (!use_drat())
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        drat_log_expr(e);
 | 
					 | 
				
			||||||
        get_drat().bool_def(v, e->get_id());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void solver::drat_log_decl(func_decl* f) {
 | 
					 | 
				
			||||||
        if (f->get_family_id() != null_family_id) 
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        if (m_drat_asts.contains(f))
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        m_drat_asts.insert(f);
 | 
					 | 
				
			||||||
        push(insert_obj_trail< ast>(m_drat_asts, f));
 | 
					 | 
				
			||||||
        std::ostringstream strm;
 | 
					 | 
				
			||||||
        smt2_pp_environment_dbg env(m);
 | 
					 | 
				
			||||||
        ast_smt2_pp(strm, f, env);
 | 
					 | 
				
			||||||
        get_drat().def_begin('f', f->get_small_id(), strm.str());
 | 
					 | 
				
			||||||
        get_drat().def_end();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -155,16 +74,19 @@ namespace euf {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::set_tmp_bool_var(bool_var b, expr* e) {
 | 
				
			||||||
 | 
					        m_bool_var2expr.setx(b, e, nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void solver::log_justification(literal l, th_explain const& jst) {
 | 
					    void solver::log_justification(literal l, th_explain const& jst) {
 | 
				
			||||||
        literal_vector lits;
 | 
					        literal_vector lits;
 | 
				
			||||||
        unsigned nv = s().num_vars();
 | 
					 | 
				
			||||||
        expr_ref_vector eqs(m);
 | 
					        expr_ref_vector eqs(m);
 | 
				
			||||||
 | 
					        unsigned nv = s().num_vars();
 | 
				
			||||||
        auto add_lit = [&](enode_pair const& eq) {
 | 
					        auto add_lit = [&](enode_pair const& eq) {
 | 
				
			||||||
            ++nv;
 | 
					            ++nv;
 | 
				
			||||||
            literal lit(nv, false);
 | 
					 | 
				
			||||||
            eqs.push_back(m.mk_eq(eq.first->get_expr(), eq.second->get_expr()));
 | 
					            eqs.push_back(m.mk_eq(eq.first->get_expr(), eq.second->get_expr()));
 | 
				
			||||||
            drat_eq_def(lit, eqs.back());            
 | 
					            set_tmp_bool_var(nv, eqs.back());
 | 
				
			||||||
            return lit;
 | 
					            return literal(nv, false);
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (auto lit : euf::th_explain::lits(jst))
 | 
					        for (auto lit : euf::th_explain::lits(jst))
 | 
				
			||||||
| 
						 | 
					@ -180,16 +102,133 @@ namespace euf {
 | 
				
			||||||
        get_drat().add(lits, sat::status::th(m_is_redundant, jst.ext().get_id(), jst.get_pragma()));
 | 
					        get_drat().add(lits, sat::status::th(m_is_redundant, jst.ext().get_id(), jst.get_pragma()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void solver::drat_eq_def(literal lit, expr* eq) {
 | 
					    void solver::on_clause(unsigned n, literal const* lits, sat::status st) {
 | 
				
			||||||
        expr *a = nullptr, *b = nullptr;
 | 
					        TRACE("euf", tout << "on-clause " << n << "\n");
 | 
				
			||||||
        VERIFY(m.is_eq(eq, a, b));
 | 
					        on_lemma(n, lits, st);
 | 
				
			||||||
        drat_log_expr(a);
 | 
					        on_proof(n, lits, st);
 | 
				
			||||||
        drat_log_expr(b);
 | 
					    }
 | 
				
			||||||
        get_drat().def_begin('e', eq->get_id(), std::string("="));
 | 
					
 | 
				
			||||||
        get_drat().def_add_arg(a->get_id());
 | 
					    void solver::on_proof(unsigned n, literal const* lits, sat::status st) {
 | 
				
			||||||
        get_drat().def_add_arg(b->get_id());
 | 
					        if (!m_proof_out)
 | 
				
			||||||
        get_drat().def_end();
 | 
					            return;
 | 
				
			||||||
        get_drat().bool_def(lit.var(), eq->get_id());
 | 
					        flet<bool> _display_all_decls(m_display_all_decls, true);
 | 
				
			||||||
 | 
					        std::ostream& out = *m_proof_out;
 | 
				
			||||||
 | 
					        if (!visit_clause(out, n, lits))
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        if (st.is_asserted()) 
 | 
				
			||||||
 | 
					            display_redundant(out, n, lits, status2proof_hint(st));
 | 
				
			||||||
 | 
					        else if (st.is_deleted()) 
 | 
				
			||||||
 | 
					            display_deleted(out, n, lits);        
 | 
				
			||||||
 | 
					        else if (st.is_redundant()) 
 | 
				
			||||||
 | 
					            display_redundant(out, n, lits, status2proof_hint(st));
 | 
				
			||||||
 | 
					        else if (st.is_input()) 
 | 
				
			||||||
 | 
					            display_assume(out, n, lits);
 | 
				
			||||||
 | 
					        else 
 | 
				
			||||||
 | 
					            UNREACHABLE();
 | 
				
			||||||
 | 
					        out.flush();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    void solver::on_lemma(unsigned n, literal const* lits, sat::status st) {
 | 
				
			||||||
 | 
					        if (!get_config().m_lemmas2console) 
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        if (!st.is_redundant() && !st.is_asserted()) 
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        std::ostream& out = std::cout;
 | 
				
			||||||
 | 
					        if (!visit_clause(out, n, lits))
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        std::function<symbol(int)> ppth = [&](int th) {
 | 
				
			||||||
 | 
					            return m.get_family_name(th);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if (!st.is_sat())
 | 
				
			||||||
 | 
					            out << "; " << sat::status_pp(st, ppth) << "\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        display_assert(out, n, lits);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::on_instantiation(unsigned n, sat::literal const* lits, unsigned k, euf::enode* const* bindings) {
 | 
				
			||||||
 | 
					        std::ostream& out = std::cout;
 | 
				
			||||||
 | 
					        for (unsigned i = 0; i < k; ++i)
 | 
				
			||||||
 | 
					            visit_expr(out, bindings[i]->get_expr());        
 | 
				
			||||||
 | 
					        VERIFY(visit_clause(out, n, lits));
 | 
				
			||||||
 | 
					        out << "(instantiate";
 | 
				
			||||||
 | 
					        display_literals(out, n, lits);
 | 
				
			||||||
 | 
					        for (unsigned i = 0; i < k; ++i) 
 | 
				
			||||||
 | 
					            display_expr(out << " :binding ", bindings[i]->get_expr());
 | 
				
			||||||
 | 
					        out << ")\n";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool solver::visit_clause(std::ostream& out, unsigned n, literal const* lits) {
 | 
				
			||||||
 | 
					        for (unsigned i = 0; i < n; ++i) {
 | 
				
			||||||
 | 
					            expr* e = bool_var2expr(lits[i].var());
 | 
				
			||||||
 | 
					            if (!e)
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            visit_expr(out, e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::display_assert(std::ostream& out, unsigned n, literal const* lits) {
 | 
				
			||||||
 | 
					        display_literals(out << "(assert (or", n, lits) << "))\n";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    void solver::display_assume(std::ostream& out, unsigned n, literal const* lits) {
 | 
				
			||||||
 | 
					        display_literals(out << "(assume", n, lits) << ")\n";        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::display_redundant(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint) {
 | 
				
			||||||
 | 
					        if (proof_hint)
 | 
				
			||||||
 | 
					            visit_expr(out, proof_hint);
 | 
				
			||||||
 | 
					        display_hint(display_literals(out << "(learn", n, lits), proof_hint) << ")\n";        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::display_deleted(std::ostream& out, unsigned n, literal const* lits) {
 | 
				
			||||||
 | 
					        display_literals(out << "(del", n, lits) << ")\n";        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ostream& solver::display_hint(std::ostream& out, expr* proof_hint) {
 | 
				
			||||||
 | 
					        if (proof_hint)
 | 
				
			||||||
 | 
					            return display_expr(out << " ", proof_hint);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            return out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expr_ref solver::status2proof_hint(sat::status st) {
 | 
				
			||||||
 | 
					        if (st.is_sat()) 
 | 
				
			||||||
 | 
					            return expr_ref(m.mk_const("rup", m.mk_proof_sort()), m); // provable by reverse unit propagation
 | 
				
			||||||
 | 
					        auto* h = reinterpret_cast<euf::th_proof_hint const*>(st.get_hint());
 | 
				
			||||||
 | 
					        if (!h)
 | 
				
			||||||
 | 
					            return expr_ref(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expr* e = h->get_hint(*this);
 | 
				
			||||||
 | 
					        if (e)
 | 
				
			||||||
 | 
					            return expr_ref(e, m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return expr_ref(m);        
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ostream& solver::display_literals(std::ostream& out, unsigned n, literal const* lits) {
 | 
				
			||||||
 | 
					        for (unsigned i = 0; i < n; ++i) {
 | 
				
			||||||
 | 
					            expr* e = bool_var2expr(lits[i].var());
 | 
				
			||||||
 | 
					            if (lits[i].sign())
 | 
				
			||||||
 | 
					                display_expr(out << " (not ", e) << ")";
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                display_expr(out << " ", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return out;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::visit_expr(std::ostream& out, expr* e) {
 | 
				
			||||||
 | 
					        m_clause_visitor.collect(e);
 | 
				
			||||||
 | 
					        if (m_display_all_decls)
 | 
				
			||||||
 | 
					            m_clause_visitor.display_decls(out);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            m_clause_visitor.display_skolem_decls(out);
 | 
				
			||||||
 | 
					        m_clause_visitor.define_expr(out, e);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::ostream& solver::display_expr(std::ostream& out, expr* e) {
 | 
				
			||||||
 | 
					        return m_clause_visitor.display_expr_def(out, e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/sat/smt/euf_proof_checker.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/sat/smt/euf_proof_checker.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2020 Microsoft Corporation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    euf_proof_checker.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Plugin manager for checking EUF proofs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Nikolaj Bjorner (nbjorner) 2020-08-25
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ast/ast_pp.h"
 | 
				
			||||||
 | 
					#include "sat/smt/euf_proof_checker.h"
 | 
				
			||||||
 | 
					#include "sat/smt/arith_proof_checker.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proof_checker::proof_checker(ast_manager& m):
 | 
				
			||||||
 | 
					        m(m) {
 | 
				
			||||||
 | 
					        arith::proof_checker* apc = alloc(arith::proof_checker, m);
 | 
				
			||||||
 | 
					        m_plugins.push_back(apc);
 | 
				
			||||||
 | 
					        apc->register_plugins(*this);
 | 
				
			||||||
 | 
					        (void)m;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proof_checker::~proof_checker() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void proof_checker::register_plugin(symbol const& rule, proof_checker_plugin* p) {
 | 
				
			||||||
 | 
					        m_map.insert(rule, p);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool proof_checker::check(expr_ref_vector const& clause, expr* e, expr_ref_vector& units) {
 | 
				
			||||||
 | 
					        if (!e || !is_app(e))
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        units.reset();
 | 
				
			||||||
 | 
					        app* a = to_app(e);
 | 
				
			||||||
 | 
					        proof_checker_plugin* p = nullptr;
 | 
				
			||||||
 | 
					        if (m_map.find(a->get_decl()->get_name(), p)) 
 | 
				
			||||||
 | 
					            return p->check(clause, a, units);
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/sat/smt/euf_proof_checker.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/sat/smt/euf_proof_checker.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,46 @@
 | 
				
			||||||
 | 
					/*++
 | 
				
			||||||
 | 
					Copyright (c) 2022 Microsoft Corporation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module Name:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    euf_proof_checker.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Abstract:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Plugin manager for checking EUF proofs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Author:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Nikolaj Bjorner (nbjorner) 2022-08-25
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--*/
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "util/map.h"
 | 
				
			||||||
 | 
					#include "util/scoped_ptr_vector.h"
 | 
				
			||||||
 | 
					#include "ast/ast.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class proof_checker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class proof_checker_plugin {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        virtual ~proof_checker_plugin() {}
 | 
				
			||||||
 | 
					        virtual bool check(expr_ref_vector const& clause, app* jst, expr_ref_vector& units) = 0;        
 | 
				
			||||||
 | 
					        virtual void register_plugins(proof_checker& pc) = 0;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class proof_checker {
 | 
				
			||||||
 | 
					        ast_manager& m;
 | 
				
			||||||
 | 
					        scoped_ptr_vector<proof_checker_plugin> m_plugins;
 | 
				
			||||||
 | 
					        map<symbol, proof_checker_plugin*, symbol_hash_proc, symbol_eq_proc> m_map;
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        proof_checker(ast_manager& m);
 | 
				
			||||||
 | 
					        ~proof_checker();
 | 
				
			||||||
 | 
					        void register_plugin(symbol const& rule, proof_checker_plugin*);
 | 
				
			||||||
 | 
					        bool check(expr_ref_vector const& clause, expr* e, expr_ref_vector& units);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,8 @@ namespace euf {
 | 
				
			||||||
        m_lookahead(nullptr),
 | 
					        m_lookahead(nullptr),
 | 
				
			||||||
        m_to_m(&m),
 | 
					        m_to_m(&m),
 | 
				
			||||||
        m_to_si(&si),
 | 
					        m_to_si(&si),
 | 
				
			||||||
        m_values(m)
 | 
					        m_values(m),
 | 
				
			||||||
 | 
					        m_clause_visitor(m)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        updt_params(p);
 | 
					        updt_params(p);
 | 
				
			||||||
        m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2);
 | 
					        m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2);
 | 
				
			||||||
| 
						 | 
					@ -347,8 +348,7 @@ namespace euf {
 | 
				
			||||||
            if (m_relevancy.enabled())
 | 
					            if (m_relevancy.enabled())
 | 
				
			||||||
                m_relevancy.propagate();
 | 
					                m_relevancy.propagate();
 | 
				
			||||||
            if (m_egraph.inconsistent()) {  
 | 
					            if (m_egraph.inconsistent()) {  
 | 
				
			||||||
                unsigned lvl = s().scope_lvl();
 | 
					                set_conflict(conflict_constraint().to_index());
 | 
				
			||||||
                s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index()));
 | 
					 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            bool propagated1 = false;
 | 
					            bool propagated1 = false;
 | 
				
			||||||
| 
						 | 
					@ -519,7 +519,7 @@ namespace euf {
 | 
				
			||||||
        bool merged = false;
 | 
					        bool merged = false;
 | 
				
			||||||
        for (unsigned i = m_egraph.nodes().size(); i-- > 0; ) {
 | 
					        for (unsigned i = m_egraph.nodes().size(); i-- > 0; ) {
 | 
				
			||||||
            euf::enode* n = m_egraph.nodes()[i];
 | 
					            euf::enode* n = m_egraph.nodes()[i];
 | 
				
			||||||
            if (!is_shared(n) || !m.is_bool(n->get_expr()))
 | 
					            if (!m.is_bool(n->get_expr()) || !is_shared(n))
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            if (n->value() == l_true && !m.is_true(n->get_root()->get_expr())) {
 | 
					            if (n->value() == l_true && !m.is_true(n->get_root()->get_expr())) {
 | 
				
			||||||
                m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var())));
 | 
					                m_egraph.merge(n, mk_true(), to_ptr(sat::literal(n->bool_var())));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,11 +60,10 @@ namespace euf {
 | 
				
			||||||
        std::ostream& display(std::ostream& out) const;
 | 
					        std::ostream& display(std::ostream& out) const;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class solver : public sat::extension, public th_internalizer, public th_decompile {
 | 
					    class solver : public sat::extension, public th_internalizer, public th_decompile, public sat::clause_eh {
 | 
				
			||||||
        typedef top_sort<euf::enode> deps_t;
 | 
					        typedef top_sort<euf::enode> deps_t;
 | 
				
			||||||
        friend class ackerman;
 | 
					        friend class ackerman;
 | 
				
			||||||
        class user_sort;
 | 
					        class user_sort;
 | 
				
			||||||
        // friend class sat::ba_solver;
 | 
					 | 
				
			||||||
        struct stats {
 | 
					        struct stats {
 | 
				
			||||||
            unsigned m_ackerman;
 | 
					            unsigned m_ackerman;
 | 
				
			||||||
            unsigned m_final_checks;
 | 
					            unsigned m_final_checks;
 | 
				
			||||||
| 
						 | 
					@ -175,20 +174,22 @@ namespace euf {
 | 
				
			||||||
        void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
 | 
					        void log_antecedents(std::ostream& out, literal l, literal_vector const& r);
 | 
				
			||||||
        void log_antecedents(literal l, literal_vector const& r);
 | 
					        void log_antecedents(literal l, literal_vector const& r);
 | 
				
			||||||
        void log_justification(literal l, th_explain const& jst);
 | 
					        void log_justification(literal l, th_explain const& jst);
 | 
				
			||||||
        void drat_log_decl(func_decl* f);
 | 
					
 | 
				
			||||||
        void drat_log_params(func_decl* f);
 | 
					        bool m_proof_initialized = false;
 | 
				
			||||||
        void drat_log_expr1(expr* n);
 | 
					        void init_proof();
 | 
				
			||||||
        ptr_vector<expr> m_drat_todo;
 | 
					        ast_pp_util m_clause_visitor;
 | 
				
			||||||
        obj_hashtable<ast> m_drat_asts;
 | 
					        bool m_display_all_decls = false;
 | 
				
			||||||
        bool m_drat_initialized{ false };
 | 
					        void on_clause(unsigned n, literal const* lits, sat::status st) override;
 | 
				
			||||||
        void init_drat();
 | 
					        void on_lemma(unsigned n, literal const* lits, sat::status st);
 | 
				
			||||||
 | 
					        void on_proof(unsigned n, literal const* lits, sat::status st);
 | 
				
			||||||
 | 
					        std::ostream& display_literals(std::ostream& out, unsigned n, sat::literal const* lits);
 | 
				
			||||||
 | 
					        void display_assume(std::ostream& out, unsigned n, literal const* lits);
 | 
				
			||||||
 | 
					        void display_redundant(std::ostream& out, unsigned n, literal const* lits, expr* proof_hint);        
 | 
				
			||||||
 | 
					        void display_deleted(std::ostream& out, unsigned n, literal const* lits);
 | 
				
			||||||
 | 
					        std::ostream& display_hint(std::ostream& out, expr* proof_hint);
 | 
				
			||||||
 | 
					        expr_ref status2proof_hint(sat::status st);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // relevancy
 | 
					        // relevancy
 | 
				
			||||||
        //bool_vector m_relevant_expr_ids;
 | 
					 | 
				
			||||||
        //bool_vector m_relevant_visited;
 | 
					 | 
				
			||||||
        //ptr_vector<expr> m_relevant_todo;
 | 
					 | 
				
			||||||
        //void init_relevant_expr_ids();
 | 
					 | 
				
			||||||
        //void push_relevant(sat::bool_var v);
 | 
					 | 
				
			||||||
        bool is_propagated(sat::literal lit);
 | 
					        bool is_propagated(sat::literal lit);
 | 
				
			||||||
        // invariant
 | 
					        // invariant
 | 
				
			||||||
        void check_eqc_bool_assignment() const;
 | 
					        void check_eqc_bool_assignment() const;
 | 
				
			||||||
| 
						 | 
					@ -341,11 +342,16 @@ namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // proof
 | 
					        // proof
 | 
				
			||||||
        bool use_drat() { return s().get_config().m_drat && (init_drat(), true); }
 | 
					        bool use_drat() { return s().get_config().m_drat && (init_proof(), true); }
 | 
				
			||||||
        sat::drat& get_drat() { return s().get_drat(); }
 | 
					        sat::drat& get_drat() { return s().get_drat(); }
 | 
				
			||||||
        void drat_bool_def(sat::bool_var v, expr* n);
 | 
					
 | 
				
			||||||
        void drat_eq_def(sat::literal lit, expr* eq);
 | 
					        void set_tmp_bool_var(sat::bool_var b, expr* e);
 | 
				
			||||||
        void drat_log_expr(expr* n);
 | 
					        bool visit_clause(std::ostream& out, unsigned n, literal const* lits);
 | 
				
			||||||
 | 
					        void display_assert(std::ostream& out, unsigned n, literal const* lits);
 | 
				
			||||||
 | 
					        void visit_expr(std::ostream& out, expr* e);
 | 
				
			||||||
 | 
					        std::ostream& display_expr(std::ostream& out, expr* e);        
 | 
				
			||||||
 | 
					        void on_instantiation(unsigned n, sat::literal const* lits, unsigned k, euf::enode* const* bindings);
 | 
				
			||||||
 | 
					        scoped_ptr<std::ostream> m_proof_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // decompile
 | 
					        // decompile
 | 
				
			||||||
        bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
 | 
					        bool extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& card,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1430,10 +1430,9 @@ namespace pb {
 | 
				
			||||||
            IF_VERBOSE(0, verbose_stream() << *c << "\n");
 | 
					            IF_VERBOSE(0, verbose_stream() << *c << "\n");
 | 
				
			||||||
        VERIFY(c->well_formed());
 | 
					        VERIFY(c->well_formed());
 | 
				
			||||||
        if (m_solver && m_solver->get_config().m_drat) {
 | 
					        if (m_solver && m_solver->get_config().m_drat) {
 | 
				
			||||||
            std::function<void(std::ostream& out)> fn = [&](std::ostream& out) {
 | 
					            auto * out = s().get_drat().out();
 | 
				
			||||||
                out << "c ba constraint " << *c << " 0\n";
 | 
					            if (out)
 | 
				
			||||||
            };
 | 
					                *out << "c ba constraint " << *c << " 0\n";
 | 
				
			||||||
            m_solver->get_drat().log_adhoc(fn);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -372,16 +372,18 @@ namespace q {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void ematch::propagate(bool is_conflict, unsigned idx, sat::ext_justification_idx j_idx) {
 | 
					    void ematch::propagate(bool is_conflict, unsigned idx, sat::ext_justification_idx j_idx) {
 | 
				
			||||||
        if (is_conflict) {
 | 
					        if (is_conflict) 
 | 
				
			||||||
            ++m_stats.m_num_conflicts;
 | 
					            ++m_stats.m_num_conflicts;
 | 
				
			||||||
            ctx.set_conflict(j_idx);
 | 
					        else
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
            ++m_stats.m_num_propagations;
 | 
					            ++m_stats.m_num_propagations;
 | 
				
			||||||
            auto& j = justification::from_index(j_idx);
 | 
					
 | 
				
			||||||
            auto lit = instantiate(j.m_clause, j.m_binding, j.m_clause[idx]);
 | 
					        auto& j = justification::from_index(j_idx);
 | 
				
			||||||
            ctx.propagate(lit, j_idx);
 | 
					        sat::literal_vector lits;
 | 
				
			||||||
        }
 | 
					        lits.push_back(~j.m_clause.m_literal);
 | 
				
			||||||
 | 
					        for (unsigned i = 0; i < j.m_clause.size(); ++i) 
 | 
				
			||||||
 | 
					            lits.push_back(instantiate(j.m_clause, j.m_binding, j.m_clause[i]));            
 | 
				
			||||||
 | 
					        m_qs.log_instantiation(lits, &j);
 | 
				
			||||||
 | 
					        m_qs.add_clause(lits);               
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool ematch::flush_prop_queue() {
 | 
					    bool ematch::flush_prop_queue() {
 | 
				
			||||||
| 
						 | 
					@ -408,6 +410,7 @@ namespace q {
 | 
				
			||||||
    void ematch::add_instantiation(clause& c, binding& b, sat::literal lit) {
 | 
					    void ematch::add_instantiation(clause& c, binding& b, sat::literal lit) {
 | 
				
			||||||
        m_evidence.reset();
 | 
					        m_evidence.reset();
 | 
				
			||||||
        ctx.propagate(lit, mk_justification(UINT_MAX, c, b.nodes()));
 | 
					        ctx.propagate(lit, mk_justification(UINT_MAX, c, b.nodes()));
 | 
				
			||||||
 | 
					        m_qs.log_instantiation(~c.m_literal, lit);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) {
 | 
					    sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,21 +47,21 @@ namespace q {
 | 
				
			||||||
            unsigned lim = m_indirect_nodes.size();
 | 
					            unsigned lim = m_indirect_nodes.size();
 | 
				
			||||||
            lit l = c[i];
 | 
					            lit l = c[i];
 | 
				
			||||||
            lbool cmp = compare(n, binding, l.lhs, l.rhs, evidence);
 | 
					            lbool cmp = compare(n, binding, l.lhs, l.rhs, evidence);
 | 
				
			||||||
 | 
					            TRACE("q", tout << l.lhs << " ~~ " << l.rhs << " is " << cmp << "\n";);
 | 
				
			||||||
            switch (cmp) {
 | 
					            switch (cmp) {
 | 
				
			||||||
            case l_false:
 | 
					            case l_false:                
 | 
				
			||||||
                m_indirect_nodes.shrink(lim);
 | 
					                m_indirect_nodes.shrink(lim);
 | 
				
			||||||
                if (!l.sign)
 | 
					                if (!l.sign)
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                c.m_watch = i;
 | 
					                c.m_watch = i;
 | 
				
			||||||
                return l_true;
 | 
					                return l_true;
 | 
				
			||||||
            case l_true:
 | 
					            case l_true:   
 | 
				
			||||||
                m_indirect_nodes.shrink(lim);
 | 
					                m_indirect_nodes.shrink(lim);
 | 
				
			||||||
                if (l.sign)
 | 
					                if (l.sign)
 | 
				
			||||||
                    break;
 | 
					                    break;                
 | 
				
			||||||
                c.m_watch = i;
 | 
					                c.m_watch = i;
 | 
				
			||||||
                return l_true;
 | 
					                return l_true;
 | 
				
			||||||
            case l_undef:
 | 
					            case l_undef:
 | 
				
			||||||
                TRACE("q", tout << l.lhs << " ~~ " << l.rhs << " is undef\n";);
 | 
					 | 
				
			||||||
                if (idx != UINT_MAX) {
 | 
					                if (idx != UINT_MAX) {
 | 
				
			||||||
                    idx = UINT_MAX;
 | 
					                    idx = UINT_MAX;
 | 
				
			||||||
                    return l_undef;
 | 
					                    return l_undef;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,6 +71,7 @@ namespace q {
 | 
				
			||||||
        for (auto const& [qlit, fml, generation] : m_instantiations) {
 | 
					        for (auto const& [qlit, fml, generation] : m_instantiations) {
 | 
				
			||||||
            euf::solver::scoped_generation sg(ctx, generation + 1);
 | 
					            euf::solver::scoped_generation sg(ctx, generation + 1);
 | 
				
			||||||
            sat::literal lit = ctx.mk_literal(fml);
 | 
					            sat::literal lit = ctx.mk_literal(fml);
 | 
				
			||||||
 | 
					            m_qs.log_instantiation(~qlit, ~lit);
 | 
				
			||||||
            m_qs.add_clause(~qlit, ~lit);
 | 
					            m_qs.add_clause(~qlit, ~lit);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        m_instantiations.reset();
 | 
					        m_instantiations.reset();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,7 @@ Author:
 | 
				
			||||||
#include "sat/smt/euf_solver.h"
 | 
					#include "sat/smt/euf_solver.h"
 | 
				
			||||||
#include "sat/smt/sat_th.h"
 | 
					#include "sat/smt/sat_th.h"
 | 
				
			||||||
#include "qe/lite/qe_lite.h"
 | 
					#include "qe/lite/qe_lite.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace q {
 | 
					namespace q {
 | 
				
			||||||
| 
						 | 
					@ -356,4 +357,10 @@ namespace q {
 | 
				
			||||||
        m_ematch.get_antecedents(l, idx, r, probing);
 | 
					        m_ematch.get_antecedents(l, idx, r, probing);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void solver::log_instantiation(unsigned n, sat::literal const* lits, justification* j) {
 | 
				
			||||||
 | 
					        TRACE("q", for (unsigned i = 0; i < n; ++i) tout << literal2expr(lits[i]) << "\n";);
 | 
				
			||||||
 | 
					        if (get_config().m_instantiations2console) {
 | 
				
			||||||
 | 
					            ctx.on_instantiation(n, lits, j ? j->m_clause.num_decls() : 0, j ? j->m_binding : nullptr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,5 +88,9 @@ namespace q {
 | 
				
			||||||
        sat::literal_vector const& universal() const { return m_universal; }
 | 
					        sat::literal_vector const& universal() const { return m_universal; }
 | 
				
			||||||
        quantifier* flatten(quantifier* q);
 | 
					        quantifier* flatten(quantifier* q);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void log_instantiation(sat::literal q, sat::literal i, justification* j = nullptr) { sat::literal lits[2] = { q, i }; log_instantiation(2, lits, j); }
 | 
				
			||||||
 | 
					        void log_instantiation(sat::literal_vector const& lits, justification* j) { log_instantiation(lits.size(), lits.data(), j); }
 | 
				
			||||||
 | 
					        void log_instantiation(unsigned n, sat::literal const* lits, justification* j);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,7 +125,7 @@ namespace euf {
 | 
				
			||||||
            pop_core(n);        
 | 
					            pop_core(n);        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sat::status th_euf_solver::mk_status(sat::proof_hint const* ps) {
 | 
					    sat::status th_euf_solver::mk_status(th_proof_hint const* ps) {
 | 
				
			||||||
        return sat::status::th(m_is_redundant, get_id(), ps);
 | 
					        return sat::status::th(m_is_redundant, get_id(), ps);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,7 +149,7 @@ namespace euf {
 | 
				
			||||||
        return add_clause(2, lits);
 | 
					        return add_clause(2, lits);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::proof_hint const* ps) {      
 | 
					    bool th_euf_solver::add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps) {      
 | 
				
			||||||
        sat::literal lits[2] = { a, b };
 | 
					        sat::literal lits[2] = { a, b };
 | 
				
			||||||
        return add_clause(2, lits, ps);
 | 
					        return add_clause(2, lits, ps);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -164,7 +164,7 @@ namespace euf {
 | 
				
			||||||
        return add_clause(4, lits);
 | 
					        return add_clause(4, lits);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, sat::proof_hint const* ps) {
 | 
					    bool th_euf_solver::add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps) {
 | 
				
			||||||
        bool was_true = false;
 | 
					        bool was_true = false;
 | 
				
			||||||
        for (unsigned i = 0; i < n; ++i)       
 | 
					        for (unsigned i = 0; i < n; ++i)       
 | 
				
			||||||
            was_true |= is_true(lits[i]);
 | 
					            was_true |= is_true(lits[i]);
 | 
				
			||||||
| 
						 | 
					@ -226,13 +226,14 @@ namespace euf {
 | 
				
			||||||
        return ctx.s().rand()();
 | 
					        return ctx.s().rand()();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    size_t th_explain::get_obj_size(unsigned num_lits, unsigned num_eqs, sat::proof_hint const* pma) {
 | 
					    size_t th_explain::get_obj_size(unsigned num_lits, unsigned num_eqs) {
 | 
				
			||||||
        return sat::constraint_base::obj_size(sizeof(th_explain) + sizeof(sat::literal) * num_lits + sizeof(enode_pair) * num_eqs + (pma?pma->to_string().length()+1:1));
 | 
					        return sat::constraint_base::obj_size(sizeof(th_explain) + sizeof(sat::literal) * num_lits + sizeof(enode_pair) * num_eqs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p, sat::proof_hint const* pma) {
 | 
					    th_explain::th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& p, th_proof_hint const* pma) {
 | 
				
			||||||
        m_consequent = c;
 | 
					        m_consequent = c;
 | 
				
			||||||
        m_eq = p;
 | 
					        m_eq = p;
 | 
				
			||||||
 | 
					        m_proof_hint = pma;
 | 
				
			||||||
        m_num_literals = n_lits;
 | 
					        m_num_literals = n_lits;
 | 
				
			||||||
        m_num_eqs = n_eqs;
 | 
					        m_num_eqs = n_eqs;
 | 
				
			||||||
        char * base_ptr = reinterpret_cast<char*>(this) + sizeof(th_explain); 
 | 
					        char * base_ptr = reinterpret_cast<char*>(this) + sizeof(th_explain); 
 | 
				
			||||||
| 
						 | 
					@ -244,33 +245,24 @@ namespace euf {
 | 
				
			||||||
        m_eqs = reinterpret_cast<enode_pair*>(base_ptr);
 | 
					        m_eqs = reinterpret_cast<enode_pair*>(base_ptr);
 | 
				
			||||||
        for (i = 0; i < n_eqs; ++i)
 | 
					        for (i = 0; i < n_eqs; ++i)
 | 
				
			||||||
            m_eqs[i] = eqs[i];
 | 
					            m_eqs[i] = eqs[i];
 | 
				
			||||||
        base_ptr += sizeof(enode_pair) * n_eqs;        
 | 
					 | 
				
			||||||
        m_pragma = reinterpret_cast<char*>(base_ptr);
 | 
					 | 
				
			||||||
        i = 0;
 | 
					 | 
				
			||||||
        if (pma) {
 | 
					 | 
				
			||||||
            std::string s = pma->to_string();
 | 
					 | 
				
			||||||
            for (i = 0; s[i]; ++i)
 | 
					 | 
				
			||||||
                m_pragma[i] = s[i];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        m_pragma[i] = 0;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, sat::proof_hint const* pma) {
 | 
					    th_explain* th_explain::mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* pma) {
 | 
				
			||||||
        region& r = th.ctx.get_region();
 | 
					        region& r = th.ctx.get_region();
 | 
				
			||||||
        void* mem = r.allocate(get_obj_size(n_lits, n_eqs, pma));
 | 
					        void* mem = r.allocate(get_obj_size(n_lits, n_eqs));
 | 
				
			||||||
        sat::constraint_base::initialize(mem, &th);
 | 
					        sat::constraint_base::initialize(mem, &th);
 | 
				
			||||||
        return new (sat::constraint_base::ptr2mem(mem)) th_explain(n_lits, lits, n_eqs, eqs, c, enode_pair(x, y));
 | 
					        return new (sat::constraint_base::ptr2mem(mem)) th_explain(n_lits, lits, n_eqs, eqs, c, enode_pair(x, y), pma);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, sat::proof_hint const* pma) {
 | 
					    th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* pma) {
 | 
				
			||||||
        return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr, pma);
 | 
					        return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), consequent, nullptr, nullptr, pma);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma) {
 | 
					    th_explain* th_explain::propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma) {
 | 
				
			||||||
        return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
 | 
					        return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma) {
 | 
					    th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma) {
 | 
				
			||||||
        return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
 | 
					        return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -313,8 +305,8 @@ namespace euf {
 | 
				
			||||||
            out << "--> " << m_consequent;
 | 
					            out << "--> " << m_consequent;
 | 
				
			||||||
        if (m_eq.first != nullptr)
 | 
					        if (m_eq.first != nullptr)
 | 
				
			||||||
            out << "--> " << m_eq.first->get_expr_id() << " == " << m_eq.second->get_expr_id();
 | 
					            out << "--> " << m_eq.first->get_expr_id() << " == " << m_eq.second->get_expr_id();
 | 
				
			||||||
        if (m_pragma != nullptr)
 | 
					        if (m_proof_hint != nullptr)
 | 
				
			||||||
            out << " p " << m_pragma;
 | 
					            out << " p ";
 | 
				
			||||||
        return out;
 | 
					        return out;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,7 @@ namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class th_decompile {
 | 
					    class th_decompile {
 | 
				
			||||||
    public:
 | 
					    public:
 | 
				
			||||||
        virtual ~th_decompile() = default;
 | 
					        virtual ~th_decompile() = default;
 | 
				
			||||||
| 
						 | 
					@ -138,6 +139,11 @@ namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class th_proof_hint : public sat::proof_hint {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        virtual expr* get_hint(euf::solver& s) const = 0;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class th_euf_solver : public th_solver {
 | 
					    class th_euf_solver : public th_solver {
 | 
				
			||||||
    protected:
 | 
					    protected:
 | 
				
			||||||
        solver& ctx;
 | 
					        solver& ctx;
 | 
				
			||||||
| 
						 | 
					@ -150,16 +156,16 @@ namespace euf {
 | 
				
			||||||
        region& get_region();
 | 
					        region& get_region();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sat::status mk_status(sat::proof_hint const* ps = nullptr);
 | 
					        sat::status mk_status(th_proof_hint const* ps = nullptr);
 | 
				
			||||||
        bool add_unit(sat::literal lit);
 | 
					        bool add_unit(sat::literal lit);
 | 
				
			||||||
        bool add_units(sat::literal_vector const& lits);
 | 
					        bool add_units(sat::literal_vector const& lits);
 | 
				
			||||||
        bool add_clause(sat::literal lit) { return add_unit(lit); }
 | 
					        bool add_clause(sat::literal lit) { return add_unit(lit); }
 | 
				
			||||||
        bool add_clause(sat::literal a, sat::literal b);
 | 
					        bool add_clause(sat::literal a, sat::literal b);
 | 
				
			||||||
        bool add_clause(sat::literal a, sat::literal b, sat::proof_hint const* ps);
 | 
					        bool add_clause(sat::literal a, sat::literal b, th_proof_hint const* ps);
 | 
				
			||||||
        bool add_clause(sat::literal a, sat::literal b, sat::literal c);
 | 
					        bool add_clause(sat::literal a, sat::literal b, sat::literal c);
 | 
				
			||||||
        bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
 | 
					        bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d);
 | 
				
			||||||
        bool add_clause(sat::literal_vector const& lits, sat::proof_hint const* ps = nullptr) { return add_clause(lits.size(), lits.data(), ps); }
 | 
					        bool add_clause(sat::literal_vector const& lits, th_proof_hint const* ps = nullptr) { return add_clause(lits.size(), lits.data(), ps); }
 | 
				
			||||||
        bool add_clause(unsigned n, sat::literal* lits, sat::proof_hint const* ps = nullptr);
 | 
					        bool add_clause(unsigned n, sat::literal* lits, th_proof_hint const* ps = nullptr);
 | 
				
			||||||
        void add_equiv(sat::literal a, sat::literal b);
 | 
					        void add_equiv(sat::literal a, sat::literal b);
 | 
				
			||||||
        void add_equiv_and(sat::literal a, sat::literal_vector const& bs);
 | 
					        void add_equiv_and(sat::literal a, sat::literal_vector const& bs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,16 +226,16 @@ namespace euf {
 | 
				
			||||||
    * that retrieve literals on demand.
 | 
					    * that retrieve literals on demand.
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
    class th_explain {
 | 
					    class th_explain {
 | 
				
			||||||
        sat::literal   m_consequent = sat::null_literal; // literal consequent for propagations
 | 
					        sat::literal     m_consequent = sat::null_literal; // literal consequent for propagations
 | 
				
			||||||
        enode_pair     m_eq = enode_pair();              // equality consequent for propagations
 | 
					        enode_pair       m_eq = enode_pair();              // equality consequent for propagations
 | 
				
			||||||
 | 
					        th_proof_hint const* m_proof_hint;
 | 
				
			||||||
        unsigned       m_num_literals;
 | 
					        unsigned       m_num_literals;
 | 
				
			||||||
        unsigned       m_num_eqs;        
 | 
					        unsigned       m_num_eqs;        
 | 
				
			||||||
        sat::literal*  m_literals;
 | 
					        sat::literal*  m_literals;
 | 
				
			||||||
        enode_pair*    m_eqs;
 | 
					        enode_pair*    m_eqs;
 | 
				
			||||||
        char*          m_pragma = nullptr;
 | 
					        static size_t get_obj_size(unsigned num_lits, unsigned num_eqs);
 | 
				
			||||||
        static size_t get_obj_size(unsigned num_lits, unsigned num_eqs, sat::proof_hint const* pma);
 | 
					        th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, th_proof_hint const* pma = nullptr);
 | 
				
			||||||
        th_explain(unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode_pair const& eq, sat::proof_hint const* pma = nullptr);
 | 
					        static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, th_proof_hint const* pma = nullptr);
 | 
				
			||||||
        static th_explain* mk(th_euf_solver& th, unsigned n_lits, sat::literal const* lits, unsigned n_eqs, enode_pair const* eqs, sat::literal c, enode* x, enode* y, sat::proof_hint const* pma = nullptr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public:
 | 
					    public:
 | 
				
			||||||
        static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs);
 | 
					        static th_explain* conflict(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs);
 | 
				
			||||||
| 
						 | 
					@ -240,9 +246,9 @@ namespace euf {
 | 
				
			||||||
        static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
 | 
					        static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
 | 
				
			||||||
        static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y);
 | 
					        static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y);
 | 
				
			||||||
        static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
 | 
					        static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
 | 
				
			||||||
        static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);
 | 
					        static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma = nullptr);
 | 
				
			||||||
        static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, sat::proof_hint const* pma = nullptr);
 | 
					        static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, th_proof_hint const* pma = nullptr);
 | 
				
			||||||
        static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);
 | 
					        static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, th_proof_hint const* pma = nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sat::ext_constraint_idx to_index() const {
 | 
					        sat::ext_constraint_idx to_index() const {
 | 
				
			||||||
            return sat::constraint_base::mem2base(this);
 | 
					            return sat::constraint_base::mem2base(this);
 | 
				
			||||||
| 
						 | 
					@ -277,7 +283,7 @@ namespace euf {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        enode_pair eq_consequent() const { return m_eq; }
 | 
					        enode_pair eq_consequent() const { return m_eq; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        sat::proof_hint const* get_pragma() const { return nullptr; } //*m_pragma ? m_pragma : nullptr; }
 | 
					        th_proof_hint const* get_pragma() const { return m_proof_hint; } 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
 | 
				
			||||||
    func_decl_ref_vector        m_unhandled_funs;
 | 
					    func_decl_ref_vector        m_unhandled_funs;
 | 
				
			||||||
    bool                        m_default_external;
 | 
					    bool                        m_default_external;
 | 
				
			||||||
    bool                        m_euf { false };
 | 
					    bool                        m_euf { false };
 | 
				
			||||||
    bool                        m_drat { false };
 | 
					 | 
				
			||||||
    bool                        m_is_redundant { false };
 | 
					    bool                        m_is_redundant { false };
 | 
				
			||||||
    bool                        m_top_level { false };
 | 
					    bool                        m_top_level { false };
 | 
				
			||||||
    sat::literal_vector         aig_lits;
 | 
					    sat::literal_vector         aig_lits;
 | 
				
			||||||
| 
						 | 
					@ -102,7 +101,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
 | 
				
			||||||
        m_ite_extra  = p.get_bool("ite_extra", true);
 | 
					        m_ite_extra  = p.get_bool("ite_extra", true);
 | 
				
			||||||
        m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX));
 | 
					        m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX));
 | 
				
			||||||
        m_euf = sp.euf();
 | 
					        m_euf = sp.euf();
 | 
				
			||||||
        m_drat = sp.drat_file().is_non_empty_string();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void throw_op_not_handled(std::string const& s) {
 | 
					    void throw_op_not_handled(std::string const& s) {
 | 
				
			||||||
| 
						 | 
					@ -169,15 +167,9 @@ struct goal2sat::imp : public sat::sat_internalizer {
 | 
				
			||||||
        if (m_expr2var_replay && m_expr2var_replay->find(n, v))
 | 
					        if (m_expr2var_replay && m_expr2var_replay->find(n, v))
 | 
				
			||||||
            return v;
 | 
					            return v;
 | 
				
			||||||
        v = m_solver.add_var(is_ext);
 | 
					        v = m_solver.add_var(is_ext);
 | 
				
			||||||
        log_def(v, n);
 | 
					 | 
				
			||||||
        return v;
 | 
					        return v;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void log_def(sat::bool_var v, expr* n) {
 | 
					 | 
				
			||||||
        if (m_drat && m_euf)
 | 
					 | 
				
			||||||
            ensure_euf()->drat_bool_def(v, n);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sat::bool_var to_bool_var(expr* e) override {
 | 
					    sat::bool_var to_bool_var(expr* e) override {
 | 
				
			||||||
        sat::literal l;
 | 
					        sat::literal l;
 | 
				
			||||||
        sat::bool_var v = m_map.to_bool_var(e);
 | 
					        sat::bool_var v = m_map.to_bool_var(e);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue