mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 19:52:29 +00:00 
			
		
		
		
	* Introduce X-macro-based trace tag definition - Created trace_tags.def to centralize TRACE tag definitions - Each tag includes a symbolic name and description - Set up enum class TraceTag for type-safe usage in TRACE macros * Add script to generate Markdown documentation from trace_tags.def - Python script parses trace_tags.def and outputs trace_tags.md * Refactor TRACE_NEW to prepend TraceTag and pass enum to is_trace_enabled * trace: improve trace tag handling system with hierarchical tagging - Introduce hierarchical tag-class structure: enabling a tag class activates all child tags - Unify TRACE, STRACE, SCTRACE, and CTRACE under enum TraceTag - Implement initial version of trace_tag.def using X(tag, tag_class, description) (class names and descriptions to be refined in a future update) * trace: replace all string-based TRACE tags with enum TraceTag - Migrated all TRACE, STRACE, SCTRACE, and CTRACE macros to use enum TraceTag values instead of raw string literals * trace : add cstring header * trace : Add Markdown documentation generation from trace_tags.def via mk_api_doc.py * trace : rename macro parameter 'class' to 'tag_class' and remove Unicode comment in trace_tags.h. * trace : Add TODO comment for future implementation of tag_class activation * trace : Disable code related to tag_class until implementation is ready (#7663).
		
			
				
	
	
		
			1185 lines
		
	
	
	
		
			43 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1185 lines
		
	
	
	
		
			43 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*++
 | |
| Copyright (c) 2015 Microsoft Corporation
 | |
| 
 | |
| Module Name:
 | |
| 
 | |
|     theory_special_relations.cpp
 | |
| 
 | |
| Abstract:
 | |
| 
 | |
|     Special Relations theory plugin.
 | |
| 
 | |
| Author:
 | |
| 
 | |
|     Nikolaj Bjorner (nbjorner) 2015-9-16
 | |
|     Ashutosh Gupta 2016
 | |
| 
 | |
| Notes:
 | |
| 
 | |
| --*/
 | |
| 
 | |
| #include <fstream>
 | |
| 
 | |
| #include "ast/reg_decl_plugins.h"
 | |
| #include "ast/datatype_decl_plugin.h"
 | |
| #include "ast/recfun_decl_plugin.h"
 | |
| #include "ast/ast_pp.h"
 | |
| #include "ast/rewriter/recfun_replace.h"
 | |
| #include "smt/smt_context.h"
 | |
| #include "smt/theory_arith.h"
 | |
| #include "smt/theory_special_relations.h"
 | |
| 
 | |
| 
 | |
| namespace smt {
 | |
| 
 | |
|     func_decl* theory_special_relations::relation::next() {
 | |
|         if (!m_next) {
 | |
|             sort* s = decl()->get_domain(0);
 | |
|             sort* domain[2] = {s, s};
 | |
|             m_next = m.mk_fresh_func_decl("specrel.next", "", 2, domain, s, false);
 | |
|         }
 | |
|         return m_next;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::relation::push() {
 | |
|         m_scopes.push_back(scope());
 | |
|         scope& s = m_scopes.back();
 | |
|         s.m_asserted_atoms_lim = m_asserted_atoms.size();
 | |
|         s.m_asserted_qhead_old = m_asserted_qhead;
 | |
|         m_graph.push();        
 | |
|         m_ufctx.get_trail_stack().push_scope();
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::relation::pop(unsigned num_scopes) {
 | |
|         unsigned new_lvl = m_scopes.size() - num_scopes;
 | |
|         scope& s = m_scopes[new_lvl];
 | |
|         m_asserted_atoms.shrink(s.m_asserted_atoms_lim);
 | |
|         m_asserted_qhead = s.m_asserted_qhead_old;
 | |
|         m_scopes.shrink(new_lvl);
 | |
|         m_graph.pop(num_scopes);        
 | |
|         m_ufctx.get_trail_stack().pop_scope(num_scopes);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::relation::ensure_var(theory_var v) {
 | |
|         while ((unsigned)v > m_uf.mk_var());
 | |
|         if ((unsigned)v >= m_graph.get_num_nodes()) {
 | |
|             m_graph.init_var(v);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::relation::new_eq_eh(literal l, theory_var v1, theory_var v2) {
 | |
|         ensure_var(v1);
 | |
|         ensure_var(v2);
 | |
|         literal_vector ls;
 | |
|         ls.push_back(l);        
 | |
|         return m_graph.add_non_strict_edge(v1, v2, ls) && m_graph.add_non_strict_edge(v2, v1, ls);
 | |
|     }
 | |
| 
 | |
|     std::ostream& theory_special_relations::relation::display(theory_special_relations const& th, std::ostream& out) const {
 | |
|         out << mk_pp(m_decl, th.get_manager());
 | |
|         for (unsigned i = 0; i < m_decl->get_num_parameters(); ++i) {
 | |
|             th.get_manager().display(out << " ", m_decl->get_parameter(i));
 | |
|         }
 | |
|         out << ":\n";
 | |
|         m_graph.display(out);
 | |
|         out << "explanation: " << m_explanation << "\n";
 | |
|         m_uf.display(out);
 | |
|         for (atom* ap : m_asserted_atoms) {
 | |
|             th.display_atom(out, *ap);
 | |
|         }
 | |
|         return out;
 | |
|     }
 | |
| 
 | |
|     theory_special_relations::theory_special_relations(context& ctx, ast_manager& m):
 | |
|         theory(ctx, m.mk_family_id("specrels")),
 | |
|         m_util(m),
 | |
|         m_can_propagate(false) {
 | |
|     }
 | |
| 
 | |
|     theory_special_relations::~theory_special_relations() {
 | |
|         reset_eh();
 | |
|     }
 | |
| 
 | |
|     theory * theory_special_relations::mk_fresh(context * new_ctx) {
 | |
|         return alloc(theory_special_relations, *new_ctx, new_ctx->get_manager());
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|        \brief for term := next(next(a,b),c) for relation f
 | |
|        assert f(term,c) or term != c
 | |
|        assert f(term,c) or term != next(a,b)
 | |
|        assert f(term,c) or term != b
 | |
|        assert f(term,c) or term != a
 | |
|      */
 | |
|     void theory_special_relations::internalize_next(func_decl* f, app* term) {
 | |
|         ast_manager& m = get_manager();
 | |
|         func_decl* nxt = term->get_decl();
 | |
|         expr* src = term->get_arg(0);
 | |
|         expr* dst = term->get_arg(1);
 | |
|         expr_ref f_rel(m.mk_app(f, src, dst), m);
 | |
|         ensure_enode(term);
 | |
|         ensure_enode(f_rel);
 | |
|         literal f_lit = ctx.get_literal(f_rel);
 | |
|         src = term;
 | |
|         while (to_app(src)->get_decl() == nxt) {
 | |
|             dst = to_app(src)->get_arg(1);
 | |
|             src = to_app(src)->get_arg(0);
 | |
|             ctx.mk_th_axiom(get_id(), f_lit, ~mk_eq(term, src, false));
 | |
|             ctx.mk_th_axiom(get_id(), f_lit, ~mk_eq(term, dst, false));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::internalize_term(app * term) {
 | |
|         m_terms.push_back(term);
 | |
|         ctx.push_trail(push_back_vector(m_terms));
 | |
|         std::stringstream strm;
 | |
|         strm << "term not not handled by special relations procedure. Use sat.smt=true " << mk_pp(term, m);
 | |
|         warning_msg(strm.str().c_str());
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::internalize_atom(app * atm, bool gate_ctx) {
 | |
|         SASSERT(m_util.is_special_relation(atm));
 | |
|         relation* r = nullptr;
 | |
|         ast_manager& m = get_manager();
 | |
|         if (!m_relations.find(atm->get_decl(), r)) {
 | |
|             r = alloc(relation, m_util.get_property(atm), atm->get_decl(), m);
 | |
|             m_relations.insert(atm->get_decl(), r);
 | |
|             for (unsigned i = 0; i < m_atoms_lim.size(); ++i) r->push();
 | |
|         }
 | |
|         expr* arg0 = atm->get_arg(0);
 | |
|         expr* arg1 = atm->get_arg(1);
 | |
|         theory_var v0 = mk_var(arg0);
 | |
|         theory_var v1 = mk_var(arg1);
 | |
|         bool_var v = ctx.mk_bool_var(atm);
 | |
|         ctx.set_var_theory(v, get_id());
 | |
|         atom* a = alloc(atom, v, *r, v0, v1);
 | |
|         m_atoms.push_back(a);
 | |
|         TRACE(special_relations, tout << mk_pp(atm, m) << " : bv" << v << " v" << a->v1() << " v" << a->v2() << ' ' << gate_ctx << "\n";);
 | |
|         m_bool_var2atom.insert(v, a);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     theory_var theory_special_relations::mk_var(expr* e) {
 | |
|         if (!ctx.e_internalized(e)) 
 | |
|             ctx.internalize(e, false);
 | |
|         enode * n = ctx.get_enode(e);
 | |
|         theory_var v = n->get_th_var(get_id());
 | |
|         if (null_theory_var == v) {
 | |
|             v = theory::mk_var(n);
 | |
|             TRACE(special_relations, tout << "v" << v << " := " << mk_pp(e, get_manager()) << "\n";);
 | |
|             ctx.attach_th_var(n, this, v);
 | |
|         }
 | |
|         return v;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::new_eq_eh(theory_var v1, theory_var v2) {
 | |
|         app* t1 = get_expr(v1);
 | |
|         app* t2 = get_expr(v2);
 | |
|         literal eq = mk_eq(t1, t2, false);
 | |
|         for (auto const& kv : m_relations) {
 | |
|             relation& r = *kv.m_value;
 | |
|             if (!r.new_eq_eh(eq, v1, v2)) {
 | |
|                 set_neg_cycle_conflict(r);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     final_check_status theory_special_relations::final_check_eh() {
 | |
|         TRACE(special_relations, tout << "\n";);
 | |
|         for (auto const& kv : m_relations) {
 | |
|             lbool r = final_check(*kv.m_value);
 | |
|             switch (r) {
 | |
|             case l_undef:
 | |
|                 return FC_GIVEUP;
 | |
|             case l_false:
 | |
|                 return FC_CONTINUE;
 | |
|             default:
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         bool new_equality = false;
 | |
|         for (auto const& kv : m_relations) {
 | |
|             if (extract_equalities(*kv.m_value)) {
 | |
|                 new_equality = true;
 | |
|             }
 | |
|             if (ctx.inconsistent()) {
 | |
|                 return FC_CONTINUE;
 | |
|             }
 | |
|         }
 | |
|         if (new_equality) {
 | |
|             return FC_CONTINUE;
 | |
|         }
 | |
|         else if (!m_terms.empty()) 
 | |
|             return FC_GIVEUP;
 | |
|         else 
 | |
|             return FC_DONE;
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::final_check_lo(relation& r) {
 | |
|         // all constraints are saturated by propagation.
 | |
|         return l_true;
 | |
|     }
 | |
| 
 | |
|     enode* theory_special_relations::ensure_enode(expr* e) {
 | |
|         if (!ctx.e_internalized(e)) {
 | |
|             ctx.internalize(e, false);
 | |
|         }
 | |
|         enode* n = ctx.get_enode(e);
 | |
|         ctx.mark_as_relevant(n);
 | |
|         return n;
 | |
|     }
 | |
| 
 | |
|     literal theory_special_relations::mk_literal(expr* _e) {
 | |
|         expr_ref e(_e, get_manager());
 | |
|         ensure_enode(e);
 | |
|         return ctx.get_literal(e);
 | |
|     }
 | |
| 
 | |
|     theory_var theory_special_relations::mk_var(enode* n) {
 | |
|         if (is_attached_to_var(n)) {
 | |
|             return n->get_th_var(get_id());
 | |
|         }
 | |
|         else {
 | |
|             theory_var v = theory::mk_var(n);
 | |
|             ctx.attach_th_var(n, this, v);
 | |
|             ctx.mark_as_relevant(n);
 | |
|             return v;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::final_check_plo(relation& r) {
 | |
|         //
 | |
|         // ensure that !Rxy -> Ryx between connected components
 | |
|         // (where Rzx & Rzy or Rxz & Ryz for some z)
 | |
|         //
 | |
|         lbool res = l_true;
 | |
|         for (unsigned i = 0; res == l_true && i < r.m_asserted_atoms.size(); ++i) {
 | |
|             atom& a = *r.m_asserted_atoms[i];
 | |
|             if (!a.phase() && r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) {
 | |
|                 res = enable(a);
 | |
|             }
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     lbool theory_special_relations::final_check_tc(relation& r) {
 | |
|         //
 | |
|         // Ensure that Rxy -> TC(R)xy
 | |
|         //
 | |
|         func_decl* tcf = r.decl();
 | |
|         func_decl* f = to_func_decl(tcf->get_parameter(0).get_ast());
 | |
|         ast_manager& m = get_manager();
 | |
|         bool new_assertion = false;
 | |
|         graph r_graph;
 | |
|         for (enode* n : ctx.enodes_of(f)) {
 | |
|             literal lit = ctx.enode2literal(n);
 | |
|             if (l_true == ctx.get_assignment(lit) && ctx.is_relevant(lit)) {
 | |
|                 expr* e = ctx.bool_var2expr(lit.var());
 | |
|                 expr* arg1 = to_app(e)->get_arg(0);
 | |
|                 expr* arg2 = to_app(e)->get_arg(1);
 | |
|                 expr_ref tc_app(m.mk_app(tcf, arg1, arg2), m);
 | |
|                 enode* tcn = ensure_enode(tc_app);
 | |
|                 if (ctx.get_assignment(tcn) != l_true) {
 | |
|                     literal consequent = ctx.get_literal(tc_app);
 | |
|                     ctx.mark_as_relevant(consequent);
 | |
|                     justification* j = ctx.mk_justification(theory_propagation_justification(get_id(), ctx, 1, &lit, consequent));
 | |
|                     TRACE(special_relations, tout << "propagate: " << tc_app << "\n";);
 | |
|                     ctx.assign(consequent, j);
 | |
|                     new_assertion = true;
 | |
|                 }
 | |
|                 else {
 | |
|                     TRACE(special_relations, tout << "add edge " << tc_app << " relevant: " << ctx.is_relevant(tcn) << "\n");
 | |
|                     theory_var v1 = get_representative(get_th_var(arg1));
 | |
|                     theory_var v2 = get_representative(get_th_var(arg2));
 | |
|                     r_graph.init_var(v1);
 | |
|                     r_graph.init_var(v2);
 | |
|                     literal_vector ls;
 | |
|                     r_graph.enable_edge(r_graph.add_edge(v1, v2, s_integer(0), ls));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // Ensure that  TC(R)xy -> Rxz1 Rz1z2 .. Rzky
 | |
|         //
 | |
|         // if not Rxy and no path in graph:
 | |
|         // Introduce next(x,y), such that
 | |
|         // TC(R)(x,y) => R(x,y) or TR(R)(next(x,y),y) & R(x,next(x,y))
 | |
|         //
 | |
|         // next(x,y) is fresh unless R(x,y) is true:
 | |
|         // R(x,y) or x != next(x,y)
 | |
|         // R(x,y) or y != next(x,y)
 | |
|         // 
 | |
|         unsigned sz = r.m_asserted_atoms.size();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             atom& a = *r.m_asserted_atoms[i];
 | |
|             if (a.phase()) {
 | |
|                 bool_var bv = a.var();
 | |
|                 expr* arg1 = get_expr(a.v1());
 | |
|                 expr* arg2 = get_expr(a.v2());
 | |
| 
 | |
|                 // we need reachability in the R graph not R* graph
 | |
|                 theory_var r1 = get_representative(a.v1());
 | |
|                 theory_var r2 = get_representative(a.v2());
 | |
|                 if (r_graph.can_reach(r1, r2)) {
 | |
|                     TRACE(special_relations, 
 | |
|                           tout << a.v1() << ": " << mk_pp(arg1, m) << " -> " 
 | |
|                           << a.v2() << ": " << mk_pp(arg2, m) << " is positive reachable\n";
 | |
|                           r.m_graph.display(tout);
 | |
|                           );
 | |
|                     continue;
 | |
|                 }
 | |
|                 expr_ref f_app(m.mk_app(f, arg1, arg2), m);   
 | |
|                 ensure_enode(f_app);
 | |
|                 literal f_lit = ctx.get_literal(f_app);
 | |
|                 ctx.mark_as_relevant(f_lit);
 | |
|                 switch (ctx.get_assignment(f_lit)) {
 | |
|                 case l_true:
 | |
|                     SASSERT(new_assertion);
 | |
|                     break;
 | |
|                 case l_false: {
 | |
|                     //
 | |
|                     // Add the axioms:
 | |
|                     //  TC(R)(x,y) => R(x,y) or TC(R)(next(x,y),y) 
 | |
|                     //  TC(R)(x,y) => R(x,y) or R(x,next(x,y))
 | |
|                     // R(x,y) or next(x,y) != x
 | |
|                     // R(x,y) or next(x,y) != y, 
 | |
|                     // and recursively on all next subterms of x.
 | |
|                     // Add the literal R(next(x,y),y) - set case split preference to true.
 | |
|                     //
 | |
|                     // TBD: perhaps replace by recursion unfolding similar to theory_rec_fun
 | |
|                     //
 | |
|                     
 | |
|                     app_ref next(r.next(arg1, arg2), m);
 | |
|                     internalize_next(f, next);
 | |
|                     expr_ref a2next(m.mk_app(f, arg1, next), m);
 | |
|                     expr_ref next2b(m.mk_app(tcf, next, arg2), m);
 | |
|                     expr_ref next_b(m.mk_app(f, next, arg2), m);
 | |
|                     ensure_enode(a2next);
 | |
|                     ensure_enode(next2b);
 | |
|                     ensure_enode(next_b);
 | |
|                     literal next2b_l = ctx.get_literal(next2b);
 | |
|                     literal a2next_l = ctx.get_literal(a2next);
 | |
|                     if (ctx.get_assignment(next2b_l) == l_true && ctx.get_assignment(a2next_l) == l_true) {
 | |
|                         break;
 | |
|                     }
 | |
|                     ctx.mk_th_axiom(get_id(), ~literal(bv), f_lit, a2next_l);
 | |
|                     ctx.mk_th_axiom(get_id(), ~literal(bv), f_lit, next2b_l);
 | |
|                     expr* nxt = next;
 | |
|                     while (r.is_next(nxt)) {
 | |
|                         expr* left  = to_app(nxt)->get_arg(0);
 | |
|                         expr* right = to_app(nxt)->get_arg(1);
 | |
|                         literal eq1 = mk_eq(next, left,  false);
 | |
|                         literal eq2 = mk_eq(next, right, false);
 | |
|                         ctx.mark_as_relevant(eq1);
 | |
|                         ctx.mark_as_relevant(eq2);
 | |
|                         ctx.assign(~eq1, nullptr);
 | |
|                         ctx.assign(~eq2, nullptr);
 | |
|                         nxt = left;
 | |
|                     }
 | |
|                     ctx.set_true_first_flag(ctx.get_literal(next_b).var());
 | |
|                     new_assertion = true;
 | |
|                     break;
 | |
|                 }
 | |
|                 case l_undef:
 | |
|                     ctx.set_true_first_flag(bv);
 | |
|                     TRACE(special_relations, tout << f_app << " is undefined\n";);
 | |
|                     new_assertion = true;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (new_assertion) {
 | |
|             TRACE(special_relations, tout << "new assertion\n";);
 | |
|             return l_false;
 | |
|         }
 | |
|         return final_check_po(r);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     lbool theory_special_relations::final_check_to(relation& r) {
 | |
|         uint_set visited, target;
 | |
|         for (atom* ap : r.m_asserted_atoms) {
 | |
|             atom& a = *ap;
 | |
|             if (a.phase()) {
 | |
|                 continue;
 | |
|             }
 | |
|             TRACE(special_relations, tout << a.v1() << " !<= " << a.v2() << "\n";);
 | |
|             target.reset();
 | |
|             theory_var w;
 | |
|             // v1 !<= v2 is asserted
 | |
|             target.insert(a.v1());
 | |
|             if (r.m_graph.reachable(a.v2(), target, visited, w)) {
 | |
|                 // we already have v2 <= v1
 | |
|                 TRACE(special_relations, tout << "already: " << a.v2() << " <= " << a.v1() << "\n";);
 | |
|                 continue;
 | |
|             }
 | |
|             if (a.v1() == a.v2()) {
 | |
|                 r.m_explanation.reset();
 | |
|                 r.m_explanation.push_back(a.explanation());
 | |
|                 set_conflict(r);                
 | |
|                 return l_false;
 | |
|             }
 | |
|             // the nodes visited from v1 become target for v2
 | |
|             if (r.m_graph.reachable(a.v2(), visited, target, w)) {
 | |
|                 //
 | |
|                 // we have the following:
 | |
|                 // v1 <= w
 | |
|                 // v2 <= w
 | |
|                 // v1 !<= v2
 | |
|                 //
 | |
|                 // enforce the assertion
 | |
|                 //
 | |
|                 //   v1 <= w & v2 <= w & v1 !<= v2 -> v2 <= v1
 | |
|                 //
 | |
|                 unsigned timestamp = r.m_graph.get_timestamp();
 | |
|                 r.m_explanation.reset();
 | |
|                 r.m_graph.find_shortest_reachable_path(a.v1(), w, timestamp, r);
 | |
|                 r.m_graph.find_shortest_reachable_path(a.v2(), w, timestamp, r);
 | |
|                 TRACE(special_relations, tout << "added edge\n";);
 | |
|                 r.m_explanation.push_back(a.explanation());
 | |
|                 literal_vector const& lits = r.m_explanation;
 | |
|                 if (!r.m_graph.add_non_strict_edge(a.v2(), a.v1(), lits)) {
 | |
|                     set_neg_cycle_conflict(r);
 | |
|                     return l_false;
 | |
|                 }
 | |
|             }
 | |
|             target.reset();
 | |
|             visited.reset();
 | |
|             target.insert(a.v2());
 | |
|             if (r.m_graph.reachable(a.v1(), target, visited, w)) {
 | |
|                 // we have v1 <= v2
 | |
|                 unsigned timestamp = r.m_graph.get_timestamp();
 | |
|                 r.m_explanation.reset();
 | |
|                 r.m_graph.find_shortest_reachable_path(a.v1(), w, timestamp, r);
 | |
|                 r.m_explanation.push_back(a.explanation());
 | |
|                 set_conflict(r);                
 | |
|             }
 | |
| 
 | |
|         }
 | |
|         return l_true;
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::enable(atom& a) {
 | |
|         if (!a.enable()) {
 | |
|             relation& r = a.get_relation();
 | |
|             set_neg_cycle_conflict(r);
 | |
|             return l_false;
 | |
|         }
 | |
|         else {
 | |
|             return l_true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::set_neg_cycle_conflict(relation& r) {
 | |
|         r.m_explanation.reset();
 | |
|         r.m_graph.traverse_neg_cycle2(false, r);
 | |
|         set_conflict(r);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::set_conflict(relation& r) {
 | |
|         literal_vector const& lits = r.m_explanation;
 | |
|         TRACE(special_relations, ctx.display_literals_verbose(tout, lits) << "\n";);
 | |
|         vector<parameter> params;
 | |
|         ctx.set_conflict(
 | |
|             ctx.mk_justification(
 | |
|                 ext_theory_conflict_justification(
 | |
|                     get_id(), ctx, 
 | |
|                     lits.size(), lits.data(), 0, nullptr, params.size(), params.data())));
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::final_check(relation& r) {
 | |
|         lbool res = propagate(r);
 | |
|         if (res != l_true) return res;
 | |
|         switch (r.m_property) {
 | |
|         case sr_lo:
 | |
|             res = final_check_lo(r);
 | |
|             break;
 | |
|         case sr_po:
 | |
|             res = final_check_po(r);
 | |
|             break;
 | |
|         case sr_plo:
 | |
|             res = final_check_plo(r);
 | |
|             break;
 | |
|         case sr_to:
 | |
|             res = final_check_to(r);
 | |
|             break;
 | |
|         case sr_tc:
 | |
|             res = final_check_tc(r);
 | |
|             break;
 | |
|         default:
 | |
|             UNREACHABLE();
 | |
|             res = l_undef;
 | |
|             break;
 | |
|         }
 | |
|         TRACE(special_relations, r.display(*this, tout << res << "\n"););
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::extract_equalities(relation& r) {
 | |
|         switch (r.m_property) {
 | |
|         case sr_tc:
 | |
|             return false;
 | |
|         default:
 | |
|             break;
 | |
|         }
 | |
|         bool new_eq = false;
 | |
|         int_vector scc_id;
 | |
|         u_map<unsigned> roots;
 | |
|         ast_manager& m = get_manager();
 | |
|         (void)m;
 | |
|         r.m_graph.compute_zero_edge_scc(scc_id);
 | |
|         int start = ctx.get_random_value();
 | |
|         for (unsigned idx = 0, j = 0; !ctx.inconsistent() && idx < scc_id.size(); ++idx) {
 | |
|             unsigned i = (start + idx) % scc_id.size();
 | |
|             if (scc_id[i] == -1) {
 | |
|                 continue;
 | |
|             }
 | |
|             enode* x = get_enode(i);
 | |
|             if (roots.find(scc_id[i], j)) {
 | |
|                 enode* y = get_enode(j);
 | |
|                 if (x->get_root() != y->get_root()) {
 | |
|                     new_eq = true;
 | |
|                     unsigned timestamp = r.m_graph.get_timestamp();
 | |
|                     r.m_explanation.reset();
 | |
|                     r.m_graph.find_shortest_zero_edge_path(i, j, timestamp, r);
 | |
|                     r.m_graph.find_shortest_zero_edge_path(j, i, timestamp, r);
 | |
|                     literal_vector const& lits = r.m_explanation;
 | |
|                     TRACE(special_relations, ctx.display_literals_verbose(tout << mk_pp(x->get_expr(), m) << " = " << mk_pp(y->get_expr(), m) << "\n", lits) << "\n";);
 | |
|                     IF_VERBOSE(20, ctx.display_literals_verbose(verbose_stream() << mk_pp(x->get_expr(), m) << " = " << mk_pp(y->get_expr(), m) << "\n", lits) << "\n";);
 | |
|                     eq_justification js(ctx.mk_justification(ext_theory_eq_propagation_justification(get_id(), ctx, lits.size(), lits.data(), 0, nullptr, 
 | |
|                                                                                                      x, y)));
 | |
|                     ctx.assign_eq(x, y, js);
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 roots.insert(scc_id[i], i);
 | |
|             }
 | |
|         }
 | |
|         return new_eq;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief Propagation for piecewise linear orders
 | |
|     */
 | |
|     lbool theory_special_relations::propagate_plo(atom& a) {
 | |
|         lbool res = l_true;
 | |
|         relation& r = a.get_relation();
 | |
|         if (a.phase()) {
 | |
|             r.m_uf.merge(a.v1(), a.v2());
 | |
|             res = enable(a);
 | |
|         }
 | |
|         else if (r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) {
 | |
|             res = enable(a);
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
|     
 | |
|     lbool theory_special_relations::propagate_po(atom& a) {
 | |
|         lbool res = l_true;
 | |
|         if (a.phase()) {
 | |
|             relation& r = a.get_relation();
 | |
|             r.m_uf.merge(a.v1(), a.v2());
 | |
|             res = enable(a);
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::propagate_tc(atom& a) {
 | |
|         if (a.phase()) {
 | |
|             VERIFY(a.enable());
 | |
|             relation& r = a.get_relation();
 | |
|             r.m_uf.merge(a.v1(), a.v2());
 | |
|         }
 | |
|         return l_true;
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::final_check_po(relation& r) {
 | |
|         for (atom* ap : r.m_asserted_atoms) {
 | |
|             atom& a = *ap;
 | |
|             if (a.phase())
 | |
|                 continue;
 | |
|             // v1 !-> v2
 | |
|             // find v1 -> v3 -> v4 -> v2 path
 | |
|             r.m_explanation.reset();
 | |
|             unsigned timestamp = r.m_graph.get_timestamp();
 | |
|             bool found_path = a.v1() == a.v2() || r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r);
 | |
|             TRACE(special_relations, tout << "check " << a.v1() << " -> " << a.v2() << " found_path: " << found_path << "\n");
 | |
|             if (found_path) {
 | |
|                 TRACE(special_relations, tout << "check po conflict\n";);
 | |
|                 r.m_explanation.push_back(a.explanation());
 | |
|                 set_conflict(r);
 | |
|                 return l_false;
 | |
|             }
 | |
|         }
 | |
|         return l_true;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::propagate() {
 | |
|         if (m_can_propagate) {
 | |
|             for (auto const& kv : m_relations) 
 | |
|                 propagate(*kv.m_value);
 | |
|             m_can_propagate = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     lbool theory_special_relations::propagate(relation& r) {
 | |
|         lbool res = l_true;
 | |
|         while (res == l_true && r.m_asserted_qhead < r.m_asserted_atoms.size()) {
 | |
|             atom& a = *r.m_asserted_atoms[r.m_asserted_qhead];
 | |
|             switch (r.m_property) {
 | |
|             case sr_lo:
 | |
|                 res = enable(a);
 | |
|                 break;
 | |
|             case sr_plo:
 | |
|                 res = propagate_plo(a);
 | |
|                 break;
 | |
|             case sr_po:
 | |
|                 res = propagate_po(a);
 | |
|                 break;
 | |
|             case sr_tc:
 | |
|                 res = propagate_tc(a);
 | |
|                 break;
 | |
|             default:
 | |
|                 if (a.phase()) {
 | |
|                     res = enable(a);
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             ++r.m_asserted_qhead;
 | |
|         }
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::reset_eh() {
 | |
|         for (auto const& kv : m_relations) {
 | |
|             dealloc(kv.m_value);
 | |
|         }
 | |
|         m_relations.reset();
 | |
|         del_atoms(0);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::assign_eh(bool_var v, bool is_true) {
 | |
|         TRACE(special_relations, tout << "assign bv" << v << " " << (is_true?" <- true":" <- false") << "\n";);
 | |
|         atom* a = m_bool_var2atom[v];
 | |
|         a->set_phase(is_true);
 | |
|         a->get_relation().m_asserted_atoms.push_back(a);
 | |
|         m_can_propagate = true;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::push_scope_eh() {
 | |
|         theory::push_scope_eh();
 | |
|         for (auto const& kv : m_relations) {
 | |
|             kv.m_value->push();
 | |
|         }
 | |
|         m_atoms_lim.push_back(m_atoms.size());
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::pop_scope_eh(unsigned num_scopes) {
 | |
|         for (auto const& kv : m_relations) {
 | |
|             kv.m_value->pop(num_scopes);
 | |
|         }
 | |
|         unsigned new_lvl = m_atoms_lim.size() - num_scopes;
 | |
|         del_atoms(m_atoms_lim[new_lvl]);
 | |
|         m_atoms_lim.shrink(new_lvl);
 | |
|         theory::pop_scope_eh(num_scopes);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::del_atoms(unsigned old_size) {
 | |
|         atoms::iterator begin = m_atoms.begin() + old_size;
 | |
|         atoms::iterator it    = m_atoms.end();
 | |
|         while (it != begin) {
 | |
|             --it;
 | |
|             atom* a = *it;
 | |
|             m_bool_var2atom.erase(a->var());
 | |
|             dealloc(a);
 | |
|         }
 | |
|         m_atoms.shrink(old_size);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void theory_special_relations::collect_statistics(::statistics & st) const {
 | |
|         for (auto const& kv : m_relations) {
 | |
|             kv.m_value->m_graph.collect_statistics(st);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     model_value_proc * theory_special_relations::mk_value(enode * n, model_generator & mg) {
 | |
|         UNREACHABLE();
 | |
|         return nullptr;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::ensure_strict(graph& g) {
 | |
|         unsigned sz = g.get_num_edges();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             if (!g.is_enabled(i)) continue;
 | |
|             if (g.get_weight(i) != s_integer(0)) continue;
 | |
|             dl_var src = g.get_source(i);
 | |
|             dl_var dst = g.get_target(i);
 | |
|             if (get_enode(src)->get_root() == get_enode(dst)->get_root()) continue;
 | |
|             VERIFY(g.add_strict_edge(src, dst, literal_vector()));
 | |
|         }
 | |
|         TRACE(special_relations, g.display(tout););
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|        src1 <= i, src2 <= i, src1 != src2 => src1 !<= src2
 | |
|      */
 | |
| 
 | |
|     void theory_special_relations::ensure_tree(graph& g) {
 | |
|         unsigned sz = g.get_num_nodes();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             int_vector const& edges = g.get_in_edges(i);
 | |
|             for (unsigned j = 0; j < edges.size(); ++j) {
 | |
|                 edge_id e1 = edges[j];
 | |
|                 if (!g.is_enabled(e1)) continue;
 | |
|                 SASSERT ((int)i == g.get_target(e1));
 | |
|                 dl_var src1 = g.get_source(e1);
 | |
|                 for (unsigned k = j + 1; k < edges.size(); ++k) {
 | |
|                     edge_id e2 = edges[k];
 | |
|                     if (!g.is_enabled(e2)) continue;
 | |
|                     dl_var src2 = g.get_source(e2);
 | |
|                     if (get_enode(src1)->get_root() != get_enode(src2)->get_root() && 
 | |
|                         disconnected(g, src1, src2)) {
 | |
|                         VERIFY(g.add_strict_edge(src1, src2, literal_vector()));
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         TRACE(special_relations, g.display(tout););
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::disconnected(graph const& g, dl_var u, dl_var v) const {
 | |
|         s_integer val_u = g.get_assignment(u);
 | |
|         s_integer val_v = g.get_assignment(v);
 | |
|         if (val_u == val_v) 
 | |
|             return u != v;
 | |
|         if (val_u < val_v) {
 | |
|             std::swap(u, v);
 | |
|             std::swap(val_u, val_v);
 | |
|         }
 | |
|         SASSERT(val_u > val_v);
 | |
|         svector<dl_var> todo;
 | |
|         todo.push_back(u);
 | |
|         while (!todo.empty()) {
 | |
|             u = todo.back();
 | |
|             todo.pop_back();
 | |
|             if (u == v) {
 | |
|                 return false;
 | |
|             }
 | |
|             SASSERT(g.get_assignment(u) <= val_u);
 | |
|             if (g.get_assignment(u) <= val_v) {
 | |
|                 continue;
 | |
|             }
 | |
|             for (edge_id e : g.get_out_edges(u)) {
 | |
|                 if (is_strict_neighbour_edge(g, e)) {
 | |
|                     todo.push_back(g.get_target(e));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     expr_ref theory_special_relations::mk_inj(relation& r, model_generator& mg) {
 | |
|         ast_manager& m = get_manager();
 | |
|         r.push();
 | |
|         ensure_strict(r.m_graph);
 | |
|         func_decl_ref fn(m);
 | |
|         expr_ref result(m);
 | |
|         arith_util arith(m);
 | |
|         sort* const* ty = r.decl()->get_domain();
 | |
|         fn = m.mk_fresh_func_decl("inj", 1, ty, arith.mk_int());
 | |
|         unsigned sz = r.m_graph.get_num_nodes();
 | |
|         func_interp* fi = alloc(func_interp, m, 1);
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             s_integer val = r.m_graph.get_assignment(i);
 | |
|             expr* arg = get_expr(i);
 | |
|             fi->insert_new_entry(&arg, arith.mk_numeral(val.to_rational(), true));
 | |
|         }
 | |
|         TRACE(special_relations, r.m_graph.display(tout););
 | |
|         r.pop(1);
 | |
|         fi->set_else(arith.mk_numeral(rational(0), true));
 | |
|         mg.get_model().register_decl(fn, fi);
 | |
|         result = arith.mk_le(m.mk_app(fn,m.mk_var(0, *ty)), m.mk_app(fn, m.mk_var(1, *ty)));
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     expr_ref theory_special_relations::mk_class(relation& r, model_generator& mg) {
 | |
|         ast_manager& m = get_manager();
 | |
|         expr_ref result(m);
 | |
|         func_decl_ref fn(m);
 | |
|         arith_util arith(m);
 | |
|         func_interp* fi = alloc(func_interp, m, 1);
 | |
|         sort* const* ty = r.decl()->get_domain();
 | |
|         fn = m.mk_fresh_func_decl("class", 1, ty, arith.mk_int());
 | |
|         unsigned sz = r.m_graph.get_num_nodes();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             unsigned val = r.m_uf.find(i);
 | |
|             expr* arg = get_expr(i);
 | |
|             fi->insert_new_entry(&arg, arith.mk_numeral(rational(val), true));
 | |
|         }
 | |
|         fi->set_else(arith.mk_numeral(rational(0), true));
 | |
|         mg.get_model().register_decl(fn, fi);
 | |
|         result = m.mk_eq(m.mk_app(fn, m.mk_var(0, *ty)), m.mk_app(fn, m.mk_var(1, *ty)));
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     expr_ref theory_special_relations::mk_interval(relation& r, model_generator& mg, unsigned_vector & lo, unsigned_vector& hi) {
 | |
|         graph const& g = r.m_graph;
 | |
|         ast_manager& m = get_manager();
 | |
|         expr_ref result(m);
 | |
|         func_decl_ref lofn(m), hifn(m);
 | |
|         arith_util arith(m);
 | |
|         func_interp* lofi = alloc(func_interp, m, 1);
 | |
|         func_interp* hifi = alloc(func_interp, m, 1);
 | |
|         sort* const* ty = r.decl()->get_domain();
 | |
|         lofn = m.mk_fresh_func_decl("lo", 1, ty, arith.mk_int());
 | |
|         hifn = m.mk_fresh_func_decl("hi", 1, ty, arith.mk_int());
 | |
|         unsigned sz = g.get_num_nodes();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             expr* arg = get_expr(i);
 | |
|             lofi->insert_new_entry(&arg, arith.mk_numeral(rational(lo[i]), true));
 | |
|             hifi->insert_new_entry(&arg, arith.mk_numeral(rational(hi[i]), true));
 | |
|         }
 | |
|         lofi->set_else(arith.mk_numeral(rational(0), true));
 | |
|         hifi->set_else(arith.mk_numeral(rational(0), true));
 | |
|         mg.get_model().register_decl(lofn, lofi);
 | |
|         mg.get_model().register_decl(hifn, hifi);
 | |
|         result = m.mk_and(arith.mk_le(m.mk_app(lofn, m.mk_var(0, *ty)), m.mk_app(lofn, m.mk_var(1, *ty))),
 | |
|                           arith.mk_le(m.mk_app(hifn, m.mk_var(1, *ty)), m.mk_app(hifn, m.mk_var(0, *ty))));
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::init_model_lo(relation& r, model_generator& m) {
 | |
|         expr_ref inj = mk_inj(r, m);
 | |
|         func_interp* fi = alloc(func_interp, get_manager(), 2);
 | |
|         fi->set_else(inj);
 | |
|         m.get_model().register_decl(r.decl(), fi);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::init_model_plo(relation& r, model_generator& mg) {
 | |
|         expr_ref inj = mk_inj(r, mg);
 | |
|         expr_ref cls = mk_class(r, mg);
 | |
|         func_interp* fi = alloc(func_interp, get_manager(), 2);
 | |
|         fi->set_else(get_manager().mk_and(inj, cls));
 | |
|         mg.get_model().register_decl(r.decl(), fi);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|        \brief model for a partial order, 
 | |
|        is a recursive function that evaluates membership in partial order over
 | |
|        a fixed set of edges. It runs in O(e*n^2) where n is the number of vertices and e 
 | |
|        number of edges.
 | |
| 
 | |
|        connected(A, dst, S) = 
 | |
|                let (A',S') = next1(a1, b1, A, next1(a2, b2, A, ... S, (nil, S)))
 | |
|                if A' = nil then false else
 | |
|                if member(dst, A') then true else
 | |
|                connected(A', dst, S')
 | |
| 
 | |
|        next1(a, b, A, S, (A',S')) = 
 | |
|               if member(a, A) and not member(b, S) then (cons(b, A'), cons(b, S')) else (A',S')
 | |
|      */
 | |
| 
 | |
| 
 | |
|     void theory_special_relations::init_model_po(relation& r, model_generator& mg, bool is_reflexive) {
 | |
|         ast_manager& m = get_manager();
 | |
|         sort* s = r.m_decl->get_domain(0);
 | |
|         datatype_util dt(m);
 | |
|         recfun::util rf(m);
 | |
|         recfun::decl::plugin& p = rf.get_plugin();
 | |
|         func_decl_ref nil(m), is_nil(m), cons(m), is_cons(m), hd(m), tl(m);        
 | |
|         sort_ref listS(dt.mk_list_datatype(s, symbol("List"), cons, is_cons, hd, tl, nil, is_nil), m);
 | |
|         func_decl_ref fst(m), snd(m), pair(m);
 | |
|         expr_ref nilc(m.mk_const(nil), m);
 | |
| 
 | |
|         expr* T = m.mk_true();
 | |
|         expr* F = m.mk_false();
 | |
| 
 | |
|         func_decl* memf, *nextf, *connectedf;
 | |
| 
 | |
|         std::string member, next, connected_sym, id;
 | |
|         auto const& pa = r.decl()->get_parameter(0);
 | |
|         if (pa.is_int())
 | |
|             id = std::to_string(pa.get_int());
 | |
|         else if (pa.is_ast() && is_func_decl(pa.get_ast()))
 | |
|             id = to_func_decl(pa.get_ast())->get_name().str();
 | |
|         else
 | |
|             throw default_exception("expected an integer or function declaration");        
 | |
|         member = "member" + id;
 | |
|         next = "next" + id;
 | |
|         connected_sym = "connected" + id;
 | |
|         {
 | |
|             sort* dom[2] = { s, listS };
 | |
|             recfun::promise_def mem = p.ensure_def(symbol(member), 2, dom, m.mk_bool_sort(), true);
 | |
|             memf = mem.get_def()->get_decl();
 | |
|             
 | |
|             var_ref xV(m.mk_var(1, s), m);
 | |
|             var_ref SV(m.mk_var(0, listS), m);
 | |
|             var_ref yV(m), vV(m), wV(m);
 | |
|             
 | |
|             expr* x = xV, *S = SV;
 | |
|             expr_ref mem_body(m);
 | |
|             mem_body = m.mk_ite(m.mk_app(is_nil, S), 
 | |
|                                 F,
 | |
|                                 m.mk_ite(m.mk_eq(m.mk_app(hd, S), x), 
 | |
|                                          T,
 | |
|                                          m.mk_app(memf, x, m.mk_app(tl, S))));            
 | |
|             recfun_replace rep(m);
 | |
|             var* vars[2] = { xV, SV };
 | |
|             p.set_definition(rep, mem, false, 2, vars, mem_body);
 | |
|         }
 | |
| 
 | |
|         sort_ref tup(dt.mk_pair_datatype(listS, listS, fst, snd, pair), m);
 | |
| 
 | |
|         {
 | |
|             sort* dom[5] = { s, s, listS, listS, tup };
 | |
|             recfun::promise_def nxt = p.ensure_def(symbol(next), 5, dom, tup, true);
 | |
|             nextf = nxt.get_def()->get_decl();
 | |
|             
 | |
|             expr_ref next_body(m);
 | |
|             var_ref aV(m.mk_var(4, s), m);
 | |
|             var_ref bV(m.mk_var(3, s), m);
 | |
|             var_ref AV(m.mk_var(2, listS), m);
 | |
|             var_ref SV(m.mk_var(1, listS), m);
 | |
|             var_ref tupV(m.mk_var(0, tup), m);
 | |
|             expr* a = aV, *b = bV, *A = AV, *S = SV, *t = tupV;
 | |
|             next_body = m.mk_ite(m.mk_and(m.mk_app(memf, a, A), m.mk_not(m.mk_app(memf, b, S))), 
 | |
|                                  m.mk_app(pair, m.mk_app(cons, b, m.mk_app(fst, t)), m.mk_app(cons, b, m.mk_app(snd, t))),
 | |
|                                  t);
 | |
| 
 | |
|             recfun_replace rep(m);
 | |
|             var* vars[5] = { aV, bV, AV, SV, tupV };
 | |
|             p.set_definition(rep, nxt, false, 5, vars, next_body);
 | |
|         }
 | |
| 
 | |
|         {
 | |
|             sort* dom[3] = { listS, s, listS };
 | |
|             recfun::promise_def connected = p.ensure_def(symbol(connected_sym), 3, dom, m.mk_bool_sort(), true);
 | |
|             connectedf = connected.get_def()->get_decl();
 | |
|             var_ref AV(m.mk_var(2, listS), m);
 | |
|             var_ref dstV(m.mk_var(1, s), m);
 | |
|             var_ref SV(m.mk_var(0, listS), m);
 | |
|             expr* A = AV, *dst = dstV, *S = SV;
 | |
|             expr_ref connected_body(m);
 | |
| 
 | |
|             connected_body = m.mk_app(pair, nilc.get(), S);
 | |
| 
 | |
|             for (atom* ap : r.m_asserted_atoms) {
 | |
|                 atom& a = *ap;
 | |
|                 if (!a.phase()) continue;
 | |
|                 SASSERT(ctx.get_assignment(a.var()) == l_true);
 | |
|                 expr* x = get_enode(a.v1())->get_root()->get_expr();
 | |
|                 expr* y = get_enode(a.v2())->get_root()->get_expr();
 | |
|                 expr* cb = connected_body;
 | |
|                 expr* args[5] = { x, y, A, S, cb };
 | |
|                 connected_body = m.mk_app(nextf, 5, args);
 | |
|             }
 | |
|             expr_ref Ap(m.mk_app(fst, connected_body.get()), m);
 | |
|             expr_ref Sp(m.mk_app(snd, connected_body.get()), m);
 | |
| 
 | |
|             connected_body = m.mk_ite(m.mk_eq(Ap, nilc), F, 
 | |
|                                       m.mk_ite(m.mk_app(memf, dst, Ap), T,
 | |
|                                                m.mk_app(connectedf, Ap, dst, Sp)));
 | |
|             
 | |
|             TRACE(special_relations, tout << connected_body << "\n";);
 | |
|             recfun_replace rep(m);
 | |
|             var* vars[3] = { AV, dstV, SV };
 | |
|             p.set_definition(rep, connected, false, 3, vars, connected_body);
 | |
|         }
 | |
| 
 | |
|         {
 | |
|             var_ref xV(m.mk_var(0, s), m);
 | |
|             var_ref yV(m.mk_var(1, s), m);
 | |
|             expr* x = xV, *y = yV;
 | |
|             
 | |
|             func_interp* fi = alloc(func_interp, m, 2);
 | |
|             expr_ref consx(m.mk_app(cons, x, nilc), m);
 | |
|             expr_ref pred(m.mk_app(connectedf, consx, y, consx), m); 
 | |
|             if (is_reflexive) {
 | |
|                 pred = m.mk_or(pred, m.mk_eq(x, y));
 | |
|             }
 | |
|             fi->set_else(pred);
 | |
|             mg.get_model().register_decl(r.decl(), fi);
 | |
|         }        
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|        \brief map each node to an interval of numbers, such that
 | |
|        the children are proper sub-intervals.
 | |
|        Then the <= relation becomes interval containment.
 | |
| 
 | |
|        1. For each vertex, count the number of nodes below it in the transitive closure.
 | |
|           Store the result in num_children.
 | |
|        2. Identify each root.
 | |
|        3. Process children, assigning unique (and disjoint) intervals.
 | |
|        4. Extract interpretation.
 | |
| 
 | |
| 
 | |
|      */
 | |
| 
 | |
|     void theory_special_relations::init_model_to(relation& r, model_generator& mg) {
 | |
|         unsigned_vector num_children, lo, hi;
 | |
|         graph const& g = r.m_graph;
 | |
|         r.push();
 | |
|         ensure_strict(r.m_graph);
 | |
|         ensure_tree(r.m_graph);
 | |
|         count_children(g, num_children);
 | |
|         assign_interval(g, num_children, lo, hi);
 | |
|         expr_ref iv = mk_interval(r, mg, lo, hi);
 | |
|         r.pop(1);
 | |
|         func_interp* fi = alloc(func_interp, get_manager(), 2);
 | |
|         fi->set_else(iv);
 | |
|         mg.get_model().register_decl(r.decl(), fi);
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::is_neighbour_edge(graph const& g, edge_id edge) const {
 | |
|         CTRACE(special_relations_verbose, g.is_enabled(edge),
 | |
|               tout << edge << ": " << g.get_source(edge) << " " << g.get_target(edge) << " ";
 | |
|               tout << (g.get_assignment(g.get_target(edge)) - g.get_assignment(g.get_source(edge))) << "\n";);
 | |
| 
 | |
|         return
 | |
|             g.is_enabled(edge) &&
 | |
|             g.get_assignment(g.get_source(edge)) - s_integer(1) == g.get_assignment(g.get_target(edge));
 | |
|     }
 | |
| 
 | |
|     bool theory_special_relations::is_strict_neighbour_edge(graph const& g, edge_id e) const {
 | |
|         return is_neighbour_edge(g, e) && g.get_weight(e) != s_integer(0);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::count_children(graph const& g, unsigned_vector& num_children) {
 | |
|         unsigned sz = g.get_num_nodes();
 | |
|         svector<dl_var> nodes;
 | |
|         num_children.resize(sz, 0);
 | |
|         bool_vector processed(sz, false);
 | |
|         for (unsigned i = 0; i < sz; ++i) nodes.push_back(i);
 | |
|         while (!nodes.empty()) {
 | |
|             dl_var v = nodes.back();
 | |
|             if (processed[v]) {
 | |
|                 nodes.pop_back();
 | |
|                 continue;
 | |
|             }
 | |
|             unsigned nc = 1;
 | |
|             bool all_p = true;
 | |
|             for (edge_id e : g.get_out_edges(v)) {
 | |
|                 if (is_strict_neighbour_edge(g, e)) {
 | |
|                     dl_var dst = g.get_target(e);
 | |
|                     TRACE(special_relations, tout << v << " -> " << dst << "\n";);
 | |
|                     if (!processed[dst]) {
 | |
|                         all_p = false;
 | |
|                         nodes.push_back(dst);
 | |
|                     }
 | |
|                     nc += num_children[dst];
 | |
|                 }
 | |
|             }
 | |
|             if (all_p) {
 | |
|                 nodes.pop_back();
 | |
|                 num_children[v] = nc;
 | |
|                 processed[v] = true;
 | |
|             }
 | |
|         }
 | |
|         TRACE(special_relations,
 | |
|               for (unsigned i = 0; i < sz; ++i) {
 | |
|                   tout << i << ": " << num_children[i] << "\n";
 | |
|               });
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::assign_interval(graph const& g, unsigned_vector const& num_children, unsigned_vector& lo, unsigned_vector& hi) {
 | |
|         svector<dl_var> nodes;
 | |
|         unsigned sz = g.get_num_nodes();
 | |
|         lo.resize(sz, 0);
 | |
|         hi.resize(sz, 0);
 | |
|         unsigned offset = 0;
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             bool is_root = true;
 | |
|             int_vector const& edges = g.get_in_edges(i);
 | |
|             for (edge_id e_id : edges) {
 | |
|                 is_root &= !g.is_enabled(e_id);
 | |
|             }
 | |
|             if (is_root) {
 | |
|                 lo[i] = offset;
 | |
|                 hi[i] = offset + num_children[i] - 1;
 | |
|                 offset = hi[i] + 1;
 | |
|                 nodes.push_back(i);
 | |
|             }
 | |
|         }
 | |
|         while (!nodes.empty()) {
 | |
|             dl_var v = nodes.back();
 | |
|             int_vector const& edges = g.get_out_edges(v);
 | |
|             unsigned l = lo[v];
 | |
|             unsigned h = hi[v];
 | |
|             (void)h;
 | |
|             nodes.pop_back();
 | |
|             for (unsigned i = 0; i < edges.size(); ++i) {
 | |
|                 SASSERT(l <= h);
 | |
|                 if (is_strict_neighbour_edge(g, edges[i])) {
 | |
|                     dl_var dst = g.get_target(edges[i]);
 | |
|                     lo[dst] = l;
 | |
|                     hi[dst] = l + num_children[dst] - 1;
 | |
|                     l = hi[dst] + 1;
 | |
|                     nodes.push_back(dst);
 | |
|                 }
 | |
|             }
 | |
|             SASSERT(l == h);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::init_model(model_generator & m) {
 | |
|         for (auto const& kv : m_relations) {
 | |
|             switch (kv.m_value->m_property) {
 | |
|             case sr_lo:
 | |
|                 init_model_lo(*kv.m_value, m);
 | |
|                 break;
 | |
|             case sr_plo:
 | |
|                 init_model_plo(*kv.m_value, m);
 | |
|                 break;
 | |
|             case sr_to:
 | |
|                 init_model_to(*kv.m_value, m);
 | |
|                 break;
 | |
|             case sr_po:
 | |
|                 init_model_po(*kv.m_value, m, true);
 | |
|                 break;
 | |
|             case sr_tc:
 | |
|                 init_model_po(*kv.m_value, m, true);                
 | |
|                 break;
 | |
|             default:
 | |
|                 // other 28 combinations of 0x1F
 | |
|                 NOT_IMPLEMENTED_YET();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::display(std::ostream & out) const {
 | |
|         if (m_relations.empty())
 | |
|             return;
 | |
|         out << "Theory Special Relations\n";
 | |
|         display_var2enode(out);
 | |
|         for (auto const& kv : m_relations) 
 | |
|             kv.m_value->display(*this, out);
 | |
|     }
 | |
| 
 | |
|     void theory_special_relations::collect_asserted_po_atoms(vector<std::pair<bool_var, bool>>& atoms) const {
 | |
|         for (auto const& kv : m_relations) {
 | |
|             relation& r = *kv.m_value;
 | |
|             if (r.m_property != sr_po) continue;
 | |
|             for (atom* ap : r.m_asserted_atoms) {
 | |
|                 atoms.push_back(std::make_pair(ap->var(), ap->phase()));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     void theory_special_relations::display_atom(std::ostream & out, atom& a) const {
 | |
|         expr* e = ctx.bool_var2expr(a.var());
 | |
|         out << (a.phase() ? "" : "(not ") << mk_pp(e, get_manager()) << (a.phase() ? "" : ")") << "\n";
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void theory_special_relations::get_specrels(func_decl_set& rels) const {
 | |
|         for (auto [f, r] : m_relations) 
 | |
|             rels.insert(r->m_decl);
 | |
|     }
 | |
| 
 | |
| }
 |