diff --git a/src/api/python/z3.py b/src/api/python/z3.py index d31bcb729..98759e81a 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -912,6 +912,7 @@ def _coerce_expr_merge(s, a): return s else: if __debug__: + _z3_assert(s1.ctx == s.ctx, "context mismatch") _z3_assert(False, "sort mismatch") else: return s @@ -1472,9 +1473,18 @@ def And(*args): >>> And(P) And(p__0, p__1, p__2, p__3, p__4) """ - args = _get_args(args) - ctx = _ctx_from_ast_arg_list(args) + last_arg = None + if len(args) > 0: + last_arg = args[len(args)-1] + if isinstance(last_arg, Context): + ctx = args[len(args)-1] + args = args[:len(args)-1] + else: + ctx = main_ctx() + args = _get_args(args) + ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: + _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_and(args, ctx) @@ -1493,9 +1503,18 @@ def Or(*args): >>> Or(P) Or(p__0, p__1, p__2, p__3, p__4) """ - args = _get_args(args) - ctx = _ctx_from_ast_arg_list(args) + last_arg = None + if len(args) > 0: + last_arg = args[len(args)-1] + if isinstance(last_arg, Context): + ctx = args[len(args)-1] + args = args[:len(args)-1] + else: + ctx = main_ctx() + args = _get_args(args) + ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: + _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") if _has_probe(args): return _probe_or(args, ctx) @@ -4147,6 +4166,7 @@ class Datatype: """ if __debug__: _z3_assert(isinstance(name, str), "String expected") + _z3_assert(name != "", "Constructor name cannot be empty") return self.declare_core(name, "is_" + name, *args) def __repr__(self): diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 6d0823a24..badf8a59d 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -599,7 +599,23 @@ namespace datalog { return 0; } result = mk_compare(OP_DL_LT, m_lt_sym, domain); - break; + break; + + case OP_DL_REP: { + if (!check_domain(0, 0, num_parameters) || + !check_domain(1, 1, arity)) return 0; + func_decl_info info(m_family_id, k, 0, 0); + result = m_manager->mk_func_decl(symbol("rep"), 1, domain, range, info); + break; + } + + case OP_DL_ABS: { + if (!check_domain(0, 0, num_parameters) || + !check_domain(1, 1, arity)) return 0; + func_decl_info info(m_family_id, k, 0, 0); + result = m_manager->mk_func_decl(symbol("abs"), 1, domain, range, info); + break; + } default: m_manager->raise_exception("operator not recognized"); diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index a662de578..65b00235c 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -48,6 +48,8 @@ namespace datalog { OP_RA_CLONE, OP_DL_CONSTANT, OP_DL_LT, + OP_DL_REP, + OP_DL_ABS, LAST_RA_OP }; diff --git a/src/ast/scoped_proof.h b/src/ast/scoped_proof.h index 68c186e85..e23a6d92a 100644 --- a/src/ast/scoped_proof.h +++ b/src/ast/scoped_proof.h @@ -16,8 +16,8 @@ Author: Revision History: --*/ -#ifndef _SCOPED_PROOF_H_ -#define _SCOPED_PROOF_H_ +#ifndef _SCOPED_PROOF__H_ +#define _SCOPED_PROOF__H_ #include "ast.h" diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 4a51e4943..08a29e381 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -189,7 +189,7 @@ class psort_app : public psort { m.inc_ref(d); m.inc_ref(num_args, args); SASSERT(num_args == m_decl->get_num_params() || m_decl->has_var_params()); - DEBUG_CODE(for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this);); + DEBUG_CODE(if (num_args == num_params) { for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this); }); } virtual void finalize(pdecl_manager & m) { diff --git a/src/duality/duality.h b/src/duality/duality.h index 4005adc1a..c315c5431 100644 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -253,6 +253,27 @@ protected: } void assert_axiom(const expr &axiom){ +#if 1 + // HACK: large "distict" predicates can kill the legacy SMT solver. + // encode them with a UIF + if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) + if(axiom.num_args() > 10){ + sort s = axiom.arg(0).get_sort(); + std::vector<sort> sv; + sv.push_back(s); + int nargs = axiom.num_args(); + std::vector<expr> args(nargs); + func_decl f = ctx->fresh_func_decl("@distinct",sv,ctx->int_sort()); + for(int i = 0; i < nargs; i++){ + expr a = axiom.arg(i); + expr new_cnstr = f(a) == ctx->int_val(i); + args[i] = new_cnstr; + } + expr cnstr = ctx->make(And,args); + islvr->AssertInterpolationAxiom(cnstr); + return; + } +#endif islvr->AssertInterpolationAxiom(axiom); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index c10d83603..59611c814 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -100,6 +100,7 @@ namespace Duality { }; Reporter *CreateStdoutReporter(RPFP *rpfp); + Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname); /** Object we throw in case of catastrophe. */ @@ -125,6 +126,7 @@ namespace Duality { { rpfp = _rpfp; reporter = 0; + conj_reporter = 0; heuristic = 0; unwinding = 0; FullExpand = false; @@ -274,6 +276,7 @@ namespace Duality { RPFP *rpfp; // the input RPFP Reporter *reporter; // object for logging + Reporter *conj_reporter; // object for logging conjectures Heuristic *heuristic; // expansion heuristic context &ctx; // Z3 context solver &slvr; // Z3 solver @@ -297,6 +300,7 @@ namespace Duality { int last_decisions; hash_set<Node *> overapproxes; std::vector<Proposer *> proposers; + std::string ConjectureFile; #ifdef BOUNDED struct Counter { @@ -310,6 +314,7 @@ namespace Duality { /** Solve the problem. */ virtual bool Solve(){ reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); + conj_reporter = ConjectureFile.empty() ? 0 : CreateConjectureFileReporter(rpfp,ConjectureFile); #ifndef LOCALIZE_CONJECTURES heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); #else @@ -340,6 +345,8 @@ namespace Duality { delete heuristic; // delete unwinding; // keep the unwinding for future mining of predicates delete reporter; + if(conj_reporter) + delete conj_reporter; for(unsigned i = 0; i < proposers.size(); i++) delete proposers[i]; return res; @@ -449,6 +456,9 @@ namespace Duality { if(option == "recursion_bound"){ return SetIntOption(RecursionBound,value); } + if(option == "conjecture_file"){ + ConjectureFile = value; + } return false; } @@ -728,6 +738,13 @@ namespace Duality { return ctx.constant(name.c_str(),ctx.bool_sort()); } + /** Make a boolean variable to act as a "marker" for a pair of nodes. */ + expr NodeMarker(Node *node1, Node *node2){ + std::string name = std::string("@m_") + string_of_int(node1->number); + name += std::string("_") + string_of_int(node2->number); + return ctx.constant(name.c_str(),ctx.bool_sort()); + } + /** Union the annotation of dst into src. If with_markers is true, we conjoin the annotation formula of dst with its marker. This allows us to discover which disjunct is @@ -1136,19 +1153,19 @@ namespace Duality { } - void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction){ + void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction, Node *other_node){ #ifdef BOUNDED if(RecursionBound >= 0 && NodePastRecursionBound(node)) return; #endif RPFP::Transformer temp = node->Annotation; - expr marker = NodeMarker(node); + expr marker = (!other_node) ? NodeMarker(node) : NodeMarker(node, other_node); temp.Formula = (!marker || temp.Formula); annot.IntersectWith(temp); marker_disjunction = marker_disjunction || marker; } - bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false){ + bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false, Node *other_node = 0){ bool res = false; annot.SetFull(); expr marker_disjunction = ctx.bool_val(false); @@ -1156,7 +1173,7 @@ namespace Duality { for(unsigned j = 0; j < insts.size(); j++){ Node *node = insts[j]; if(indset->Contains(insts[j])){ - GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction); res = true; + GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction, other_node); res = true; } } annot.Formula = annot.Formula && marker_disjunction; @@ -1253,7 +1270,7 @@ namespace Duality { Node *inst = insts[k]; if(indset->Contains(inst)){ if(checker->Empty(node) || - eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst)),ctx.bool_val(true))){ + eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst,node)),ctx.bool_val(true))){ candidate.Children.push_back(inst); goto next_child; } @@ -1336,7 +1353,7 @@ namespace Duality { for(unsigned j = 0; j < edge->Children.size(); j++){ Node *oc = edge->Children[j]; Node *nc = gen_cands_edge->Children[j]; - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); + GenNodeSolutionWithMarkers(oc,nc->Annotation,true,nc); } checker->AssertEdge(gen_cands_edge,1,true); return root; @@ -1462,6 +1479,8 @@ namespace Duality { bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ if(!node->Annotation.SubsetEq(fact)){ reporter->Update(node,fact,eager); + if(conj_reporter) + conj_reporter->Update(node,fact,eager); indset->Update(node,fact); updated_nodes.insert(node->map); node->Annotation.IntersectWith(fact); @@ -2201,7 +2220,7 @@ namespace Duality { #endif int expand_max = 1; if(0&&duality->BatchExpand){ - int thing = static_cast<int>(stack.size() * 0.1); + int thing = stack.size() / 10; // * 0.1; expand_max = std::max(1,thing); if(expand_max > 1) std::cout << "foo!\n"; @@ -3043,6 +3062,7 @@ namespace Duality { }; }; + static int stop_event = -1; class StreamReporter : public Reporter { std::ostream &s; @@ -3052,6 +3072,9 @@ namespace Duality { int event; int depth; void ev(){ + if(stop_event == event){ + std::cout << "stop!\n"; + } s << "[" << event++ << "]" ; } virtual void Extend(RPFP::Node *node){ @@ -3129,4 +3152,28 @@ namespace Duality { Reporter *CreateStdoutReporter(RPFP *rpfp){ return new StreamReporter(rpfp, std::cout); } + + class ConjectureFileReporter : public Reporter { + std::ofstream s; + public: + ConjectureFileReporter(RPFP *_rpfp, const std::string &fname) + : Reporter(_rpfp), s(fname.c_str()) {} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ + s << "(define-fun " << node->Name.name() << " ("; + for(unsigned i = 0; i < update.IndParams.size(); i++){ + if(i != 0) + s << " "; + s << "(" << update.IndParams[i] << " " << update.IndParams[i].get_sort() << ")"; + } + s << ") Bool \n"; + s << update.Formula << ")\n"; + s << std::endl; + } + }; + + Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname){ + return new ConjectureFileReporter(rpfp, fname); + } + } + diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index a0975a719..0e367129d 100644 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -397,6 +397,11 @@ namespace Duality { sort array_domain() const; sort array_range() const; + + friend std::ostream & operator<<(std::ostream & out, sort const & m){ + m.ctx().print_expr(out,m); + return out; + } }; diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 32fe33566..8f98a23f9 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -260,7 +260,7 @@ void iz3base::check_interp(const std::vector<ast> &itps, std::vector<ast> &theor #endif } -bool iz3base::is_sat(const std::vector<ast> &q, ast &_proof){ +bool iz3base::is_sat(const std::vector<ast> &q, ast &_proof, std::vector<ast> &vars){ params_ref p; p.set_bool("proof", true); // this is currently useless @@ -277,6 +277,15 @@ bool iz3base::is_sat(const std::vector<ast> &q, ast &_proof){ ::ast *proof = s.get_proof(); _proof = cook(proof); } + else if(vars.size()) { + model_ref(_m); + s.get_model(_m); + for(unsigned i = 0; i < vars.size(); i++){ + expr_ref r(m()); + _m.get()->eval(to_expr(vars[i].raw()),r,true); + vars[i] = cook(r.get()); + } + } dealloc(m_solver); return res != l_false; } diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h index 550c563af..956191290 100755 --- a/src/interp/iz3base.h +++ b/src/interp/iz3base.h @@ -113,7 +113,7 @@ class iz3base : public iz3mgr, public scopes { void check_interp(const std::vector<ast> &itps, std::vector<ast> &theory); /** For convenience -- is this formula SAT? */ - bool is_sat(const std::vector<ast> &consts, ast &_proof); + bool is_sat(const std::vector<ast> &consts, ast &_proof, std::vector<ast> &vars); /** Interpolator for clauses, to be implemented */ virtual void interpolate_clause(std::vector<ast> &lits, std::vector<ast> &itps){ diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index 7616b8664..5d401d49f 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -29,6 +29,10 @@ Revision History: #ifndef IZ3_HASH_H #define IZ3_HASH_H +#ifdef _WINDOWS +#pragma warning(disable:4267) +#endif + #include <string> #include <vector> #include <iterator> diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 4ef594cee..3ec2c42d1 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -387,10 +387,13 @@ class iz3mgr { return UnknownTheory; } - enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,UnknownKind}; + enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,ArithMysteryKind,UnknownKind}; lemma_kind get_theory_lemma_kind(const ast &proof){ symb s = sym(proof); + if(s->get_num_parameters() < 2) { + return ArithMysteryKind; // Bad -- Z3 hasn't told us + } ::symbol p0; bool ok = s->get_parameter(1).is_symbol(p0); if(!ok) return UnknownKind; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 5fe3338dc..e89aa1d6d 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -607,7 +607,29 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + ast distribute_coeff(const ast &coeff, const ast &s){ + if(sym(s) == sum){ + if(sym(arg(s,2)) == sum) + return make(sum, + distribute_coeff(coeff,arg(s,0)), + make_int(rational(1)), + distribute_coeff(make(Times,coeff,arg(s,1)), arg(s,2))); + else + return make(sum, + distribute_coeff(coeff,arg(s,0)), + make(Times,coeff,arg(s,1)), + arg(s,2)); + } + if(op(s) == Leq && arg(s,1) == make_int(rational(0)) && arg(s,2) == make_int(rational(0))) + return s; + return make(sum,make(Leq,make_int(rational(0)),make_int(rational(0))),coeff,s); + } + ast simplify_sum(std::vector<ast> &args){ + if(args[1] != make_int(rational(1))){ + if(sym(args[2]) == sum) + return make(sum,args[0],make_int(rational(1)),distribute_coeff(args[1],args[2])); + } ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); if(!is_normal_ineq(ineq)) throw cannot_simplify(); @@ -757,6 +779,22 @@ class iz3proof_itp_impl : public iz3proof_itp { ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); // if(!is_true(Aproves1) || !is_true(Bproves1)) // std::cout << "foo!\n";; + if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ + if(get_term_type(arg(x,0)) == LitA){ + ast iter = z3_simplify(make(Plus,arg(x,0),get_ineq_rhs(xleqy))); + ast rewrite1 = make_rewrite(LitA,pos_add(0,top_pos),Acond,make(Equal,arg(x,0),iter)); + iter = make(Plus,iter,arg(x,1)); + ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); + return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); + } + if(get_term_type(arg(x,1)) == LitA){ + ast iter = z3_simplify(make(Plus,arg(x,1),get_ineq_rhs(xleqy))); + ast rewrite1 = make_rewrite(LitA,pos_add(1,top_pos),Acond,make(Equal,arg(x,1),iter)); + iter = make(Plus,arg(x,0),iter); + ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); + return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); + } + } if(get_term_type(x) == LitA){ ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,iter)); @@ -1014,6 +1052,7 @@ class iz3proof_itp_impl : public iz3proof_itp { coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); break; case Not: + coeff = make_int(rational(-1)); case Plus: break; case Times: @@ -2568,12 +2607,17 @@ class iz3proof_itp_impl : public iz3proof_itp { break; default: { // mixed equality if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ - // std::cerr << "WARNING: mixed term in leq2eq\n"; - std::vector<ast> lits; - lits.push_back(con); - lits.push_back(make(Not,xleqy)); - lits.push_back(make(Not,yleqx)); - return make_axiom(lits); + if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ + // std::cerr << "WARNING: untested case in leq2eq\n"; + } + else { + // std::cerr << "WARNING: mixed term in leq2eq\n"; + std::vector<ast> lits; + lits.push_back(con); + lits.push_back(make(Not,xleqy)); + lits.push_back(make(Not,yleqx)); + return make_axiom(lits); + } } std::vector<ast> conjs; conjs.resize(3); conjs[0] = mk_not(con); @@ -2655,8 +2699,13 @@ class iz3proof_itp_impl : public iz3proof_itp { }; std::vector<LocVar> localization_vars; // localization vars in order of creation - hash_map<ast,ast> localization_map; // maps terms to their localization vars - hash_map<ast,ast> localization_pf_map; // maps terms to proofs of their localizations + + struct locmaps { + hash_map<ast,ast> localization_map; // maps terms to their localization vars + hash_map<ast,ast> localization_pf_map; // maps terms to proofs of their localizations + }; + + hash_map<prover::range,locmaps> localization_maps_per_range; /* "localize" a term e to a given frame range, creating new symbols to represent non-local subterms. This returns the localized version e_l, @@ -2677,7 +2726,24 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(Equal,x,y); } + bool range_is_global(const prover::range &r){ + if(pv->range_contained(r,rng)) + return false; + if(!pv->ranges_intersect(r,rng)) + return false; + return true; + } + ast localize_term(ast e, const prover::range &rng, ast &pf){ + + // we need to memoize this function separately for A, B and global + prover::range map_range = rng; + if(range_is_global(map_range)) + map_range = pv->range_full(); + locmaps &maps = localization_maps_per_range[map_range]; + hash_map<ast,ast> &localization_map = maps.localization_map; + hash_map<ast,ast> &localization_pf_map = maps.localization_pf_map; + ast orig_e = e; pf = make_refl(e); // proof that e = e @@ -2764,6 +2830,21 @@ class iz3proof_itp_impl : public iz3proof_itp { ast bar = make_assumption(frame,foo); pf = make_transitivity(new_var,e,orig_e,bar,pf); localization_pf_map[orig_e] = pf; + + // HACK: try to bias this term in the future + if(!pv->range_is_full(rng)){ + prover::range rf = pv->range_full(); + locmaps &fmaps = localization_maps_per_range[rf]; + hash_map<ast,ast> &flocalization_map = fmaps.localization_map; + hash_map<ast,ast> &flocalization_pf_map = fmaps.localization_pf_map; + // if(flocalization_map.find(orig_e) == flocalization_map.end()) + { + flocalization_map[orig_e] = new_var; + flocalization_pf_map[orig_e] = pf; + } + } + + return new_var; } diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h index cd7203eeb..bf93f4726 100755 --- a/src/interp/iz3scopes.h +++ b/src/interp/iz3scopes.h @@ -23,6 +23,7 @@ Revision History: #include <vector> #include <limits.h> +#include "iz3hash.h" class scopes { @@ -63,6 +64,11 @@ class scopes { return rng.hi < rng.lo; } + /** is this range full? */ + bool range_is_full(const range &rng){ + return rng.lo == SHRT_MIN && rng.hi == SHRT_MAX; + } + /** return an empty range */ range range_empty(){ range res; @@ -194,4 +200,23 @@ class scopes { }; + +// let us hash on ranges + +#ifndef FULL_TREE +namespace hash_space { + template <> + class hash<scopes::range> { + public: + size_t operator()(const scopes::range &p) const { + return (size_t)p.lo + (size_t)p.hi; + } + }; +} + +inline bool operator==(const scopes::range &x, const scopes::range &y){ + return x.lo == y.lo && x.hi == y.hi; +} +#endif + #endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index a7c2125e7..26786d57a 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1058,36 +1058,66 @@ public: } + rational get_first_coefficient(const ast &t, ast &v){ + if(op(t) == Plus){ + unsigned best_id = UINT_MAX; + rational best_coeff(0); + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + if(op(arg(t,i)) != Numeral){ + ast lv = get_linear_var(arg(t,i)); + unsigned id = ast_id(lv); + if(id < best_id) { + v = lv; + best_id = id; + best_coeff = get_coeff(arg(t,i)); + } + } + return best_coeff; + } + else + if(op(t) != Numeral) + return(get_coeff(t)); + return rational(0); + } + ast divide_inequalities(const ast &x, const ast&y){ - std::vector<rational> xcoeffs,ycoeffs; - get_linear_coefficients(arg(x,1),xcoeffs); - get_linear_coefficients(arg(y,1),ycoeffs); - if(xcoeffs.size() != ycoeffs.size() || xcoeffs.size() == 0) + ast xvar, yvar; + rational xcoeff = get_first_coefficient(arg(x,0),xvar); + rational ycoeff = get_first_coefficient(arg(y,0),yvar); + if(xcoeff == rational(0) || ycoeff == rational(0) || xvar != yvar) + throw "bad assign-bounds lemma"; + rational ratio = xcoeff/ycoeff; + if(denominator(ratio) != rational(1)) throw "bad assign-bounds lemma"; - rational ratio = xcoeffs[0]/ycoeffs[0]; return make_int(ratio); // better be integer! } ast AssignBounds2Farkas(const ast &proof, const ast &con){ std::vector<ast> farkas_coeffs; get_assign_bounds_coeffs(proof,farkas_coeffs); - std::vector<ast> lits; int nargs = num_args(con); if(nargs != (int)(farkas_coeffs.size())) throw "bad assign-bounds theory lemma"; #if 0 - for(int i = 1; i < nargs; i++) - lits.push_back(mk_not(arg(con,i))); - ast sum = sum_inequalities(farkas_coeffs,lits); - ast conseq = rhs_normalize_inequality(arg(con,0)); - ast d = divide_inequalities(sum,conseq); - std::vector<ast> my_coeffs; - my_coeffs.push_back(d); - for(unsigned i = 0; i < farkas_coeffs.size(); i++) - my_coeffs.push_back(farkas_coeffs[i]); + if(farkas_coeffs[0] != make_int(rational(1))) + farkas_coeffs[0] = make_int(rational(1)); #else - std::vector<ast> my_coeffs; + std::vector<ast> lits, lit_coeffs; + for(int i = 1; i < nargs; i++){ + lits.push_back(mk_not(arg(con,i))); + lit_coeffs.push_back(farkas_coeffs[i]); + } + ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); + ast conseq = normalize_inequality(arg(con,0)); + ast d = divide_inequalities(sum,conseq); +#if 0 + if(d != farkas_coeffs[0]) + std::cout << "wow!\n"; #endif + farkas_coeffs[0] = d; +#endif + std::vector<ast> my_coeffs; std::vector<ast> my_cons; for(int i = 1; i < nargs; i++){ my_cons.push_back(mk_not(arg(con,i))); @@ -1107,10 +1137,27 @@ public: ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector<Iproof::node> prems){ std::vector<ast> farkas_coeffs; get_assign_bounds_rule_coeffs(proof,farkas_coeffs); - std::vector<ast> lits; int nargs = num_prems(proof)+1; if(nargs != (int)(farkas_coeffs.size())) throw "bad assign-bounds theory lemma"; +#if 0 + if(farkas_coeffs[0] != make_int(rational(1))) + farkas_coeffs[0] = make_int(rational(1)); +#else + std::vector<ast> lits, lit_coeffs; + for(int i = 1; i < nargs; i++){ + lits.push_back(conc(prem(proof,i-1))); + lit_coeffs.push_back(farkas_coeffs[i]); + } + ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); + ast conseq = normalize_inequality(con); + ast d = divide_inequalities(sum,conseq); +#if 0 + if(d != farkas_coeffs[0]) + std::cout << "wow!\n"; +#endif + farkas_coeffs[0] = d; +#endif std::vector<ast> my_coeffs; std::vector<ast> my_cons; for(int i = 1; i < nargs; i++){ @@ -1278,6 +1325,17 @@ public: return make(Plus,args); } + void get_sum_as_vector(const ast &t, std::vector<rational> &coeffs, std::vector<ast> &vars){ + if(!(op(t) == Plus)){ + coeffs.push_back(get_coeff(t)); + vars.push_back(get_linear_var(t)); + } + else { + int nargs = num_args(t); + for(int i = 0; i < nargs; i++) + get_sum_as_vector(arg(t,i),coeffs,vars); + } + } ast replace_summands_with_fresh_vars(const ast &t, hash_map<ast,ast> &map){ if(op(t) == Plus){ @@ -1294,6 +1352,99 @@ public: return map[t]; } + rational lcd(const std::vector<rational> &rats){ + rational res = rational(1); + for(unsigned i = 0; i < rats.size(); i++){ + res = lcm(res,denominator(rats[i])); + } + return res; + } + + Iproof::node reconstruct_farkas_with_dual(const std::vector<ast> &prems, const std::vector<Iproof::node> &pfs, const ast &con){ + int nprems = prems.size(); + std::vector<ast> npcons(nprems); + hash_map<ast,ast> pain_map; // not needed + for(int i = 0; i < nprems; i++){ + npcons[i] = painfully_normalize_ineq(conc(prems[i]),pain_map); + if(op(npcons[i]) == Lt){ + ast constval = z3_simplify(make(Sub,arg(npcons[i],1),make_int(rational(1)))); + npcons[i] = make(Leq,arg(npcons[i],0),constval); + } + } + ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); + npcons.push_back(ncon); + + hash_map<ast,ast> dual_map; + std::vector<ast> cvec, vars_seen; + ast rhs = make_real(rational(0)); + for(unsigned i = 0; i < npcons.size(); i++){ + ast c= mk_fresh_constant("@c",real_type()); + cvec.push_back(c); + ast lhs = arg(npcons[i],0); + std::vector<rational> coeffs; + std::vector<ast> vars; + get_sum_as_vector(lhs,coeffs,vars); + for(unsigned j = 0; j < coeffs.size(); j++){ + rational coeff = coeffs[j]; + ast var = vars[j]; + if(dual_map.find(var) == dual_map.end()){ + dual_map[var] = make_real(rational(0)); + vars_seen.push_back(var); + } + ast foo = make(Plus,dual_map[var],make(Times,make_real(coeff),c)); + dual_map[var] = foo; + } + rhs = make(Plus,rhs,make(Times,c,arg(npcons[i],1))); + } + std::vector<ast> cnstrs; + for(unsigned i = 0; i < vars_seen.size(); i++) + cnstrs.push_back(make(Equal,dual_map[vars_seen[i]],make_real(rational(0)))); + cnstrs.push_back(make(Leq,rhs,make_real(rational(0)))); + for(unsigned i = 0; i < cvec.size() - 1; i++) + cnstrs.push_back(make(Geq,cvec[i],make_real(rational(0)))); + cnstrs.push_back(make(Equal,cvec.back(),make_real(rational(1)))); + ast new_proof; + + // greedily reduce the core + for(unsigned i = 0; i < cvec.size() - 1; i++){ + std::vector<ast> dummy; + cnstrs.push_back(make(Equal,cvec[i],make_real(rational(0)))); + if(!is_sat(cnstrs,new_proof,dummy)) + cnstrs.pop_back(); + } + + std::vector<ast> vals = cvec; + if(!is_sat(cnstrs,new_proof,vals)) + throw "Proof error!"; + std::vector<rational> rat_farkas_coeffs; + for(unsigned i = 0; i < cvec.size(); i++){ + ast bar = vals[i]; + rational r; + if(is_numeral(bar,r)) + rat_farkas_coeffs.push_back(r); + else + throw "Proof error!"; + } + rational the_lcd = lcd(rat_farkas_coeffs); + std::vector<ast> farkas_coeffs; + std::vector<Iproof::node> my_prems; + std::vector<ast> my_pcons; + for(unsigned i = 0; i < prems.size(); i++){ + ast fc = make_int(rat_farkas_coeffs[i] * the_lcd); + if(!(fc == make_int(rational(0)))){ + farkas_coeffs.push_back(fc); + my_prems.push_back(pfs[i]); + my_pcons.push_back(conc(prems[i])); + } + } + farkas_coeffs.push_back(make_int(the_lcd)); + my_prems.push_back(iproof->make_hypothesis(mk_not(con))); + my_pcons.push_back(mk_not(con)); + + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); + return res; + } + ast painfully_normalize_ineq(const ast &ineq, hash_map<ast,ast> &map){ ast res = normalize_inequality(ineq); ast lhs = arg(res,0); @@ -1318,7 +1469,8 @@ public: npcons.push_back(ncon); // ast assumps = make(And,pcons); ast new_proof; - if(is_sat(npcons,new_proof)) + std::vector<ast> dummy; + if(is_sat(npcons,new_proof,dummy)) throw "Proof error!"; pfrule dk = pr(new_proof); int nnp = num_prems(new_proof); @@ -1334,7 +1486,7 @@ public: farkas_coeffs.push_back(make_int(rational(1))); } else - throw "cannot reconstruct farkas proof"; + return reconstruct_farkas_with_dual(prems,pfs,con); for(int i = 0; i < nnp; i++){ ast p = conc(prem(new_proof,i)); @@ -1348,7 +1500,7 @@ public: my_pcons.push_back(mk_not(con)); } else - throw "cannot reconstruct farkas proof"; + return reconstruct_farkas_with_dual(prems,pfs,con); } Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; @@ -1378,7 +1530,8 @@ public: npcons.push_back(ncon); // ast assumps = make(And,pcons); ast new_proof; - if(is_sat(npcons,new_proof)) + std::vector<ast> dummy; + if(is_sat(npcons,new_proof,dummy)) throw "Proof error!"; pfrule dk = pr(new_proof); int nnp = num_prems(new_proof); @@ -1408,7 +1561,7 @@ public: my_pcons.push_back(mk_not(con)); } else - throw "cannot reconstruct farkas proof"; + return painfully_reconstruct_farkas(prems,pfs,con); } Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); return res; @@ -1433,6 +1586,12 @@ public: return res; } + ast ArithMysteryRule(const ast &con, const std::vector<ast> &prems, const std::vector<Iproof::node> &args){ + // Hope for the best! + Iproof::node guess = reconstruct_farkas(prems,args,con); + return guess; + } + struct CannotCombineEqPropagate {}; void CombineEqPropagateRec(const ast &proof, std::vector<ast> &prems, std::vector<Iproof::node> &args, ast &eqprem){ @@ -1552,6 +1711,13 @@ public: if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; + // no idea why this shows up + if(dk == PR_MODUS_PONENS_OEQ) + if(conc(prem(proof,0)) == con){ + res = translate_main(prem(proof,0),expect_clause); + return res; + } + #if 0 if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ Iproof::node clause = translate_main(prem(proof,0),true); @@ -1737,6 +1903,14 @@ public: res = EqPropagate(con,prems,args); break; } + case ArithMysteryKind: { + // Z3 hasn't told us what kind of lemma this is -- maybe we can guess + std::vector<ast> prems(nprems); + for(unsigned i = 0; i < nprems; i++) + prems[i] = prem(proof,i); + res = ArithMysteryRule(con,prems,args); + break; + } default: throw unsupported(); } diff --git a/src/math/simplex/simplex.h b/src/math/simplex/simplex.h index 3f164f275..86429b1a2 100644 --- a/src/math/simplex/simplex.h +++ b/src/math/simplex/simplex.h @@ -174,10 +174,11 @@ namespace simplex { var_t select_pivot_core(var_t x_i, bool is_below, scoped_numeral& out_a_ij); int get_num_non_free_dep_vars(var_t x_j, int best_so_far); - var_t pick_var_to_leave(var_t x_j, bool inc, scoped_eps_numeral& gain, scoped_numeral& new_a_ij); + var_t pick_var_to_leave(var_t x_j, bool is_pos, + scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc); - void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc); + void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j); bool at_lower(var_t v) const; diff --git a/src/math/simplex/simplex_def.h b/src/math/simplex/simplex_def.h index c1a4ec36e..cfd557b33 100644 --- a/src/math/simplex/simplex_def.h +++ b/src/math/simplex/simplex_def.h @@ -646,13 +646,13 @@ namespace simplex { scoped_eps_numeral delta(em); scoped_numeral a_ij(m); var_t x_i, x_j; - bool inc; + bool inc_x_i, inc_x_j; while (true) { if (m_cancel) { return l_undef; } - select_pivot_primal(v, x_i, x_j, a_ij, inc); + select_pivot_primal(v, x_i, x_j, a_ij, inc_x_i, inc_x_j); if (x_j == null_var) { // optimal return l_true; @@ -660,12 +660,12 @@ namespace simplex { TRACE("simplex", tout << "x_i: v" << x_i << " x_j: v" << x_j << "\n";); var_info& vj = m_vars[x_j]; if (x_i == null_var) { - if (inc && vj.m_upper_valid) { + if (inc_x_j && vj.m_upper_valid) { delta = vj.m_upper; delta -= vj.m_value; update_value(x_j, delta); } - else if (!inc && vj.m_lower_valid) { + else if (!inc_x_j && vj.m_lower_valid) { delta = vj.m_lower; delta -= vj.m_value; update_value(x_j, delta); @@ -686,7 +686,7 @@ namespace simplex { pivot(x_i, x_j, a_ij); TRACE("simplex", display(tout << "after pivot\n");); - move_to_bound(x_i, !inc); + move_to_bound(x_i, !inc_x_i); SASSERT(well_formed_row(row(m_vars[x_j].m_base2row))); TRACE("simplex", display(tout);); SASSERT(is_feasible()); @@ -705,7 +705,7 @@ namespace simplex { em.sub(vi.m_upper, vi.m_value, delta); } TRACE("simplex", tout << "move " << (to_lower?"to_lower":"to_upper") - << " v" << x << " " << em.to_string(delta) << "\n";); + << " v" << x << " delta: " << em.to_string(delta) << "\n";); col_iterator it = M.col_begin(x), end = M.col_end(x); for (; it != end && is_pos(delta); ++it) { // @@ -756,10 +756,11 @@ namespace simplex { x_i - base variable of row(x_i) to become non-base x_j - variable in row(v) to make a base variable a_ij - coefficient to x_j in row(x_i) - inc - whether to increment x_j (true if coefficient in row(v) is negative). + inc - whether to increment x_i */ template<typename Ext> - void simplex<Ext>::select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc) { + void simplex<Ext>::select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, + bool& inc_x_i, bool& inc_x_j) { row r(m_vars[v].m_base2row); row_iterator it = M.row_begin(r), end = M.row_end(r); @@ -767,25 +768,27 @@ namespace simplex { scoped_numeral new_a_ij(m); x_i = null_var; x_j = null_var; - inc = false; + inc_x_i = false; + bool inc_y = false; for (; it != end; ++it) { var_t x = it->m_var; if (x == v) continue; - bool is_pos = m.is_pos(it->m_coeff) == m.is_pos(m_vars[v].m_base_coeff); - if ((is_pos && at_upper(x)) || (!is_pos && at_lower(x))) { - TRACE("simplex", tout << "v" << x << " pos: " << is_pos + bool inc_x = m.is_pos(it->m_coeff) == m.is_pos(m_vars[v].m_base_coeff); + if ((inc_x && at_upper(x)) || (!inc_x && at_lower(x))) { + TRACE("simplex", tout << "v" << x << " pos: " << inc_x << " at upper: " << at_upper(x) << " at lower: " << at_lower(x) << "\n";); continue; // variable cannot be used for improving bounds. // TBD check? - } - var_t y = pick_var_to_leave(x, is_pos, new_gain, new_a_ij); + } + var_t y = pick_var_to_leave(x, inc_x, new_gain, new_a_ij, inc_y); if (y == null_var) { // unbounded. x_i = y; x_j = x; - inc = is_pos; + inc_x_i = inc_y; + inc_x_j = inc_x; a_ij = new_a_ij; break; } @@ -794,20 +797,39 @@ namespace simplex { ((is_zero(new_gain) && is_zero(gain) && (x_i == null_var || y < x_i))); if (better) { + TRACE("simplex", + em.display(tout << "gain:", gain); + em.display(tout << " new gain:", new_gain); + tout << " base x_i: " << y << ", new base x_j: " << x << ", inc x_j: " << inc_x << "\n";); + x_i = y; x_j = x; - inc = is_pos; + inc_x_i = inc_y; + inc_x_j = inc_x; gain = new_gain; a_ij = new_a_ij; } } } + // + // y is a base variable. + // v is a base variable. + // v*a_v + x*a_x + E = 0 + // y*b_y + x*b_x + F = 0 + // inc(x) := sign(a_v) == sign(a_x) + // sign_eq := sign(b_y) == sign(b_x) + // sign_eq => (inc(x) != inc(y)) + // !sign_eq => (inc(x) = inc(y)) + // -> + // inc(y) := sign_eq != inc(x) + // + template<typename Ext> typename simplex<Ext>::var_t simplex<Ext>::pick_var_to_leave( - var_t x_j, bool inc, - scoped_eps_numeral& gain, scoped_numeral& new_a_ij) { + var_t x_j, bool inc_x_j, + scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc_x_i) { var_t x_i = null_var; gain.reset(); scoped_eps_numeral curr_gain(em); @@ -818,10 +840,13 @@ namespace simplex { var_info& vi = m_vars[s]; numeral const& a_ij = it.get_row_entry().m_coeff; numeral const& a_ii = vi.m_base_coeff; - bool inc_s = (m.is_pos(a_ii) != m.is_pos(a_ij)) ? inc : !inc; - TRACE("simplex", tout << "v" << x_j << " base v" << s << " incs: " << inc_s - << " upper valid:" << vi.m_upper_valid - << " lower valid:" << vi.m_lower_valid << "\n"; + bool sign_eq = (m.is_pos(a_ii) == m.is_pos(a_ij)); + bool inc_s = sign_eq != inc_x_j; + TRACE("simplex", tout << "x_j: v" << x_j << ", base x_i: v" << s + << ", inc_x_i: " << inc_s + << ", inc_x_j: " << inc_x_j + << ", upper valid:" << vi.m_upper_valid + << ", lower valid:" << vi.m_lower_valid << "\n"; display_row(tout, r);); if ((inc_s && !vi.m_upper_valid) || (!inc_s && !vi.m_lower_valid)) { continue; @@ -841,6 +866,7 @@ namespace simplex { x_i = s; gain = curr_gain; new_a_ij = a_ij; + inc_x_i = inc_s; TRACE("simplex", tout << "x_j v" << x_j << " x_i v" << x_i << " gain: "; tout << curr_gain << "\n";); } diff --git a/src/model/model.cpp b/src/model/model.cpp index 4a9767cb0..a6b9695b4 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -133,7 +133,9 @@ bool model::eval(expr * e, expr_ref & result, bool model_completion) { ev(e, result); return true; } - catch (model_evaluator_exception &) { + catch (model_evaluator_exception & ex) { + (void)ex; + TRACE("model_evaluator", tout << ex.msg() << "\n";); return false; } } diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 51b54bc01..1952cc42f 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -278,6 +278,12 @@ namespace datalog { void register_variable(func_decl* var); + /* + Replace constants that have been registered as + variables by de-Bruijn indices and corresponding + universal (if is_forall is true) or existential + quantifier. + */ expr_ref bind_variables(expr* fml, bool is_forall); /** diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index e201c2a19..5bfbba6b2 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -77,6 +77,7 @@ def_module_params('fixedpoint', ('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'), ('batch_expand', BOOL, False, 'DUALITY: use batch expansion'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), + ('conjecture_file', STRING, '', 'DUALITY: save conjectures to file'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 587a184bc..0584e6b58 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -35,6 +35,7 @@ Revision History: #include "model_smt2_pp.h" #include "model_v2_pp.h" #include "fixedpoint_params.hpp" +#include "used_vars.h" // template class symbol_table<family_id>; @@ -164,6 +165,20 @@ lbool dl_interface::query(::expr * query) { clauses.push_back(e); } + std::vector<sort> b_sorts; + std::vector<symbol> b_names; + used_vars uv; + uv.process(query); + unsigned nuv = uv.get_max_found_var_idx_plus_1(); + for(int i = nuv-1; i >= 0; i--){ // var indices are backward + ::sort * s = uv.get(i); + if(!s) + s = _d->ctx.m().mk_bool_sort(); // missing var, whatever + b_sorts.push_back(sort(_d->ctx,s)); + b_names.push_back(symbol(_d->ctx,::symbol(i))); // names? + } + +#if 0 // turn the query into a clause expr q(_d->ctx,m_ctx.bind_variables(query,false)); @@ -177,6 +192,9 @@ lbool dl_interface::query(::expr * query) { } q = q.arg(0); } +#else + expr q(_d->ctx,query); +#endif expr qc = implies(q,_d->ctx.bool_val(false)); qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); @@ -211,6 +229,7 @@ lbool dl_interface::query(::expr * query) { rs->SetOption("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0"); rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "1" : "0"); rs->SetOption("batch_expand",m_ctx.get_params().batch_expand() ? "1" : "0"); + rs->SetOption("conjecture_file",m_ctx.get_params().conjecture_file()); unsigned rb = m_ctx.get_params().recursion_bound(); if(rb != UINT_MAX){ std::ostringstream os; os << rb; @@ -350,7 +369,9 @@ void dl_interface::display_certificate_non_const(std::ostream& out) { if(_d->status == StatusModel){ ast_manager &m = m_ctx.get_manager(); model_ref md = get_model(); + out << "(fixedpoint \n"; model_smt2_pp(out, m, *md.get(), 0); + out << ")\n"; } else if(_d->status == StatusRefutation){ out << "(derivation\n"; diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index c7a8d5aa0..cdb4a5b9e 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -205,7 +205,6 @@ namespace datalog { for (unsigned i = 0; i < rules.size(); ++i) { app* head = rules[i]->get_head(); expr_ref_vector conj(m); - unsigned n = head->get_num_args()-1; for (unsigned j = 0; j < head->get_num_args(); ++j) { expr* arg = head->get_arg(j); if (!is_var(arg)) { diff --git a/src/opt/hitting_sets.cpp b/src/opt/hitting_sets.cpp new file mode 100644 index 000000000..7ef2eadc6 --- /dev/null +++ b/src/opt/hitting_sets.cpp @@ -0,0 +1,370 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + hitting_sets.h + +Abstract: + + Hitting set approximations. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-06-06 + +Notes: + +--*/ +#include "vector.h" +#include "util.h" +#include "hitting_sets.h" +#include "simplex.h" +#include "sparse_matrix_def.h" +#include "simplex_def.h" + +typedef simplex::simplex<simplex::mpz_ext> Simplex; +typedef simplex::sparse_matrix<simplex::mpz_ext> sparse_matrix; + + +namespace opt { + + struct hitting_sets::imp { + typedef unsigned_vector set; + volatile bool m_cancel; + rational m_lower; + rational m_upper; + vector<rational> m_weights; + rational m_max_weight; + rational m_denominator; + vector<set> m_S; + svector<lbool> m_value; + unsigned_vector m_value_trail; + unsigned_vector m_value_lim; + vector<unsigned_vector> m_use_list; + unsynch_mpz_manager m; + Simplex m_simplex; + unsigned m_weights_var; + + imp():m_cancel(false) {} + ~imp() {} + + void add_weight(rational const& w) { + SASSERT(w.is_pos()); + unsigned var = m_weights.size(); + m_simplex.ensure_var(var); + m_simplex.set_lower(var, mpq_inf(mpq(0),mpq(0))); + m_simplex.set_upper(var, mpq_inf(mpq(1),mpq(0))); + m_weights.push_back(w); + m_value.push_back(l_undef); + m_use_list.push_back(unsigned_vector()); + m_max_weight += w; + } + + void add_set(unsigned sz, unsigned const* S) { + if (sz == 0) { + return; + } + for (unsigned i = 0; i < sz; ++i) { + m_use_list[S[i]].push_back(m_S.size()); + } + init_weights(); + m_S.push_back(unsigned_vector(sz, S)); + add_simplex_row(sz, S); + } + + bool compute_lower() { + m_lower.reset(); + return L1() && L2() && L3(); + } + + bool compute_upper() { + m_upper = m_max_weight; + return U1(); + } + + rational get_lower() { + return m_lower/m_denominator; + } + + rational get_upper() { + return m_upper/m_denominator; + } + + void set_cancel(bool f) { + m_cancel = f; + m_simplex.set_cancel(f); + } + + void collect_statistics(::statistics& st) const { + m_simplex.collect_statistics(st); + } + + void reset() { + m_lower.reset(); + m_upper = m_max_weight; + } + + void init_weights() { + if (m_weights_var != 0) { + return; + } + m_weights_var = m_weights.size(); + unsigned_vector vars; + scoped_mpz_vector coeffs(m); + + // normalize weights to integral. + rational d(1); + for (unsigned i = 0; i < m_weights.size(); ++i) { + d = lcm(d, denominator(m_weights[i])); + } + m_denominator = d; + if (!d.is_one()) { + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_weights[i] *= d; + } + } + // set up Simplex objective function. + for (unsigned i = 0; i < m_weights.size(); ++i) { + vars.push_back(i); + coeffs.push_back(m_weights[i].to_mpq().numerator()); + } + m_simplex.ensure_var(m_weights_var); + vars.push_back(m_weights_var); + coeffs.push_back(mpz(-1)); + m_simplex.add_row(m_weights_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); + } + + struct scoped_select { + imp& s; + unsigned sz; + scoped_select(imp& s):s(s), sz(s.m_value_trail.size()) { + } + ~scoped_select() { + s.undo_select(sz); + } + }; + + struct value_lt { + vector<rational> const& weights; + unsigned_vector const& scores; + value_lt(vector<rational> const& weights, unsigned_vector const& scores): + weights(weights), scores(scores) {} + bool operator()(int v1, int v2) const { + // - score1 / w1 < - score2 / w2 + // <=> + // score1 / w1 > score2 / w2 + // <=> + // score1*w2 > score2*w1 + unsigned score1 = scores[v1]; + unsigned score2 = scores[v2]; + rational w1 = weights[v1]; + rational w2 = weights[v2]; + return rational(score1)*w2 > rational(score2)*w1; + } + }; + + + // compute upper bound for hitting set. + bool U1() { + rational w(0); + scoped_select _sc(*this); + + // score each variable by the number of + // unassigned sets they occur in. + unsigned_vector scores; + init_scores(scores); + + // + // Sort indices. + // The least literals are those where -score/w is minimized. + // + unsigned_vector indices; + for (unsigned i = 0; i < m_value.size(); ++i) { + indices.push_back(i); + } + value_lt lt(m_weights, scores); + while (!m_cancel) { + std::sort(indices.begin(), indices.end(), lt); + unsigned idx = indices[0]; + if (scores[idx] == 0) { + break; + } + update_scores(scores, idx); + select(idx); + w += m_weights[idx]; + } + if (w < m_upper) { + m_upper = w; + } + return !m_cancel; + } + + void init_scores(unsigned_vector & scores) { + scores.reset(); + for (unsigned i = 0; i < m_value.size(); ++i) { + scores.push_back(0); + } + for (unsigned i = 0; i < m_S.size(); ++i) { + set const& S = m_S[i]; + if (!has_selected(S)) { + for (unsigned j = 0; j < S.size(); ++j) { + scores[S[j]]++; + } + } + } + } + + void update_scores(unsigned_vector& scores, unsigned v) { + unsigned_vector const& v_uses = m_use_list[v]; + for (unsigned i = 0; i < v_uses.size(); ++i) { + set const& S = m_S[v_uses[i]]; + if (!has_selected(S)) { + for (unsigned j = 0; j < S.size(); ++j) { + --scores[S[j]]; + } + } + } + } + + bool L1() { + rational w(0); + scoped_select _sc(*this); + for (unsigned i = 0; !m_cancel && i < m_S.size(); ++i) { + set const& S = m_S[i]; + SASSERT(!S.empty()); + if (!has_selected(S)) { + w += m_weights[select_min(S)]; + for (unsigned j = 0; j < S.size(); ++j) { + select(S[j]); + } + } + } + if (m_lower < w) { + m_lower = w; + } + return !m_cancel; + } + + bool L2() { + rational w(0); + scoped_select _sc(*this); + int n = 0; + for (unsigned i = 0; i < m_S.size(); ++i) { + if (!has_selected(m_S[i])) ++n; + } + unsigned_vector scores; + init_scores(scores); + unsigned_vector indices; + for (unsigned i = 0; i < m_value.size(); ++i) { + indices.push_back(i); + } + value_lt lt(m_weights, scores); + + std::sort(indices.begin(), indices.end(), lt); + for(unsigned i = 0; i < indices.size() && n > 0; ++i) { + // deg(c) = score(c) + // wt(c) = m_weights[c] + unsigned idx = indices[i]; + if (scores[idx] == 0) { + break; + } + if (scores[idx] < static_cast<unsigned>(n) || m_weights[idx].is_one()) { + w += m_weights[idx]; + } + else { + w += div((rational(n)*m_weights[idx]), rational(scores[idx])); + } + n -= scores[idx]; + } + if (m_lower < w) { + m_lower = w; + } + return !m_cancel; + } + + bool L3() { + TRACE("simplex", m_simplex.display(tout);); + VERIFY(l_true == m_simplex.make_feasible()); + TRACE("simplex", m_simplex.display(tout);); + VERIFY(l_true == m_simplex.minimize(m_weights_var)); + mpq_inf const& val = m_simplex.get_value(m_weights_var); + unsynch_mpq_inf_manager mg; + unsynch_mpq_manager& mq = mg.mpq_manager(); + scoped_mpq c(mq); + mg.ceil(val, c); + rational w = rational(c); + if (w > m_lower) { + m_lower = w; + } + return true; + } + + void add_simplex_row(unsigned sz, unsigned const* S) { + unsigned_vector vars; + scoped_mpz_vector coeffs(m); + for (unsigned i = 0; i < sz; ++i) { + vars.push_back(S[i]); + coeffs.push_back(mpz(1)); + } + unsigned base_var = m_S.size() + m_weights.size(); + m_simplex.ensure_var(base_var); + vars.push_back(base_var); + coeffs.push_back(mpz(-1)); + // S - base_var = 0 + // base_var >= 1 + m_simplex.set_lower(base_var, mpq_inf(mpq(1),mpq(0))); + m_simplex.add_row(base_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); + } + + void undo_select(unsigned sz) { + for (unsigned j = sz; j < m_value_trail.size(); ++j) { + m_value[m_value_trail[j]] = l_undef; + } + m_value_trail.resize(sz); + } + + unsigned select_min(set const& S) { + unsigned result = S[0]; + for (unsigned i = 1; i < S.size(); ++i) { + if (m_weights[result] > m_weights[S[i]]) { + result = S[i]; + } + } + return result; + } + + + lbool selected(unsigned j) const { + return m_value[j]; + } + + void select(unsigned j) { + m_value[j] = l_true; + m_value_trail.push_back(j); + } + + bool has_selected(set const& S) { + for (unsigned i = 0; i < S.size(); ++i) { + if (l_true == selected(S[i])) { + return true; + } + } + return false; + } + + }; + hitting_sets::hitting_sets() { m_imp = alloc(imp); } + hitting_sets::~hitting_sets() { dealloc(m_imp); } + void hitting_sets::add_weight(rational const& w) { m_imp->add_weight(w); } + void hitting_sets::add_set(unsigned sz, unsigned const* elems) { m_imp->add_set(sz, elems); } + bool hitting_sets::compute_lower() { return m_imp->compute_lower(); } + bool hitting_sets::compute_upper() { return m_imp->compute_upper(); } + rational hitting_sets::get_lower() { return m_imp->get_lower(); } + rational hitting_sets::get_upper() { return m_imp->get_upper(); } + void hitting_sets::set_cancel(bool f) { m_imp->set_cancel(f); } + void hitting_sets::collect_statistics(::statistics& st) const { m_imp->collect_statistics(st); } + void hitting_sets::reset() { m_imp->reset(); } + + +}; diff --git a/src/opt/hitting_sets.h b/src/opt/hitting_sets.h new file mode 100644 index 000000000..bea5559f9 --- /dev/null +++ b/src/opt/hitting_sets.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + hitting_sets.h + +Abstract: + + Hitting set approximations. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-06-06 + +Notes: + +--*/ +#ifndef _HITTING_SETS_H_ +#define _HITTING_SETS_H_ + +#include "rational.h" +#include "statistics.h" + +namespace opt { + + class hitting_sets { + struct imp; + imp* m_imp; + public: + hitting_sets(); + ~hitting_sets(); + void add_weight(rational const& w); + void add_set(unsigned sz, unsigned const* elems); + bool compute_lower(); + bool compute_upper(); + rational get_lower(); + rational get_upper(); + void set_cancel(bool f); + void collect_statistics(::statistics& st) const; + void reset(); + }; + + +}; + +#endif diff --git a/src/opt/weighted_maxsat.cpp b/src/opt/weighted_maxsat.cpp index 6c4bbc1b4..12a922b01 100644 --- a/src/opt/weighted_maxsat.cpp +++ b/src/opt/weighted_maxsat.cpp @@ -38,8 +38,8 @@ Notes: #include "cancel_eh.h" #include "scoped_timer.h" #include "optsmt.h" +#include "hitting_sets.h" -#define USE_SIMPLEX 0 namespace opt { @@ -614,9 +614,7 @@ namespace opt { }; scoped_ptr<maxsmt_solver_base> maxs; - optsmt m_optsmt; // hitting set optimizer based on simplex. - opt_solver m_solver; - unsigned m_objective; // index of objective + hitting_sets m_hs; expr_ref_vector m_aux; // auxiliary (indicator) variables. expr_ref_vector m_iaux; // auxiliary integer (indicator) variables. expr_ref_vector m_naux; // negation of auxiliary variables. @@ -628,31 +626,29 @@ namespace opt { pb_util pb; arith_util a; stats m_stats; + bool m_at_lower_bound; public: hsmax(solver* s, ast_manager& m, maxsmt_solver_base* maxs): maxsmt_solver_base(s, m), maxs(maxs), - m_optsmt(m), - m_solver(m, m_params, symbol()), m_aux(m), m_iaux(m), m_naux(m), pb(m), - a(m) { + a(m), + m_at_lower_bound(false) { } virtual ~hsmax() {} virtual void set_cancel(bool f) { maxsmt_solver_base::set_cancel(f); maxs->set_cancel(f); - m_optsmt.set_cancel(f); } virtual void updt_params(params_ref& p) { maxsmt_solver_base::updt_params(p); - m_solver.updt_params(p); } virtual void collect_statistics(statistics& st) const { @@ -685,8 +681,8 @@ namespace opt { if (m_cancel) { return l_undef; } - lbool core_found = generate_cores(hs); + lbool core_found = generate_cores(hs); switch(core_found) { case l_undef: return l_undef; @@ -698,20 +694,20 @@ namespace opt { break; case l_false: TRACE("opt", tout << "no more seeds\n";); - m_lower = m_upper; - return l_true; + m_lower = m_upper; + return l_true; case l_undef: return l_undef; - } - break; + } + break; } - case l_false: + case l_false: TRACE("opt", tout << "no more cores\n";); m_lower = m_upper; return l_true; - } - } - return l_true; + } + } + return l_true; } private: @@ -740,26 +736,16 @@ namespace opt { m_aux_active.push_back(false); m_core_activity.push_back(0); m_aux2index.insert(m_aux.back(), i); -#if USE_SIMPLEX - m_aux2index.insert(m_iaux.back(), i); - fml = m.mk_and(a.mk_le(a.mk_numeral(rational::zero(), true), iaux), - a.mk_le(iaux, a.mk_numeral(rational::one(), true))); - rational const& w = m_weights[i]; - sum.push_back(a.mk_mul(a.mk_numeral(w, w.is_int()), iaux)); - m_solver.assert_expr(fml); -#endif if (tt) { m_asms.push_back(m_aux.back()); ensure_active(i); } } -#if USE_SIMPLEX - obj = a.mk_add(sum.size(), sum.c_ptr()); - m_objective = m_optsmt.add(obj); - m_optsmt.setup(m_solver); -#else maxs->init_soft(m_weights, m_aux); -#endif + + for (unsigned i = 0; i < m_weights.size(); ++i) { + m_hs.add_weight(m_weights[i]); + } TRACE("opt", print_seed(tout);); } @@ -895,54 +881,51 @@ namespace opt { } } + lbool next_seed(ptr_vector<expr>& hs, lbool core_found) { + + if (core_found == l_false && m_at_lower_bound) { + return l_true; + } + lbool is_sat = next_seed(); + switch(is_sat) { + case l_true: + seed2hs(false, hs); + return m_at_lower_bound?l_true:l_false; + case l_false: + TRACE("opt", tout << "no more seeds\n";); + return l_true; + case l_undef: + return l_undef; + } + return l_undef; + } + // // retrieve the next seed that satisfies state of maxs. // state of maxs must be satisfiable before optimization is called. // - // // find a satisfying assignment to maxs state, that // minimizes objective function. // lbool next_seed() { scoped_stopwatch _sw(m_stats.m_aux_sat_time); TRACE("opt", tout << "\n";); -#if USE_SIMPLEX - m_solver.display(std::cout); - lbool is_sat = m_optsmt.lex(m_objective); - if (is_sat == l_true) { - model_ref mdl; - m_optsmt.get_model(mdl); - - for (unsigned i = 0; i < num_soft(); ++i) { - if (is_active(i)) { - m_seed[i] = is_one(mdl, m_iaux[i].get()); - } - else { - m_seed[i] = false; - } - } - print_seed(std::cout); - TRACE("opt", print_seed(tout);); - } - -#else // min c_i*(not x_i) for x_i are soft clauses. // max c_i*x_i for x_i are soft clauses lbool is_sat = l_true; + m_at_lower_bound = false; + expr_ref fml(m); if (m_lower.is_pos()) { - expr_ref fml(m); solver::scoped_push _scope(maxs->s()); fml = pb.mk_le(num_soft(), m_weights.c_ptr(), m_naux.c_ptr(), m_lower); maxs->add_hard(fml); - //fml = pb.mk_ge(num_soft(), m_weights.c_ptr(), m_naux.c_ptr(), m_lower); - //maxs->add_hard(fml); - std::cout << fml << "\n"; is_sat = maxs->s().check_sat(0,0); if (is_sat == l_true) { maxs->set_model(); extract_seed(); + m_at_lower_bound = true; return l_true; } } @@ -951,17 +934,32 @@ namespace opt { maxs->set_model(); } else { + m_at_lower_bound = true; return is_sat; } - is_sat = (*maxs)(); - + is_sat = (*maxs)(); + if (is_sat == l_true) { extract_seed(); } -#endif return is_sat; } +#if 0 + if (!m_hs.compute_upper()) { + return l_undef; + } + solver::scoped_push _scope(maxs->s()); + fml = pb.mk_le(num_soft(), m_weights.c_ptr(), m_naux.c_ptr(), m_hs.get_upper()); + IF_VERBOSE(0, verbose_stream() << "upper: " << m_hs.get_upper() << " " << m_upper << "\n";); + maxs->add_hard(fml); + TRACE("opt", tout << "checking with upper bound: " << m_hs.get_upper() << "\n";); + is_sat = maxs->s().check_sat(0,0); + std::cout << is_sat << "\n"; + + // TBD: uper bound estimate does not include the negative constraints. +#endif + void extract_seed() { model_ref mdl; maxs->get_model(mdl); @@ -1140,16 +1138,6 @@ namespace opt { } expr_ref_vector fmls(m); expr_ref fml(m); -#if USE_SIMPLEX - for (unsigned i = 0; i < num_soft(); ++i) { - if (!indices.contains(i)) { - fmls.push_back(m_iaux[i].get()); - } - } - fml = a.mk_ge(a.mk_add(fmls.size(), fmls.c_ptr()), a.mk_numeral(rational::one(), true)); - m_solver.assert_expr(fml); - -#else for (unsigned i = 0; i < num_soft(); ++i) { if (!indices.contains(i)) { fmls.push_back(m_aux[i].get()); @@ -1158,7 +1146,6 @@ namespace opt { fml = m.mk_or(fmls.size(), fmls.c_ptr()); maxs->add_hard(fml); set_upper(); -#endif TRACE("opt", tout << fml << "\n";); } @@ -1174,25 +1161,17 @@ namespace opt { void block_up() { expr_ref_vector fmls(m); expr_ref fml(m); -#if USE_SIMPLEX - for (unsigned i = 0; i < m_asms.size(); ++i) { - unsigned index = m_aux2index.find(m_asms[i]); - m_core_activity[index]++; - fmls.push_back(m_iaux[index].get()); - } - fml = a.mk_lt(a.mk_add(fmls.size(), fmls.c_ptr()), a.mk_numeral(rational(fmls.size()), true)); - TRACE("opt", tout << fml << "\n";); - m_solver.assert_expr(fml); -#else + unsigned_vector indices; for (unsigned i = 0; i < m_asms.size(); ++i) { unsigned index = m_aux2index.find(m_asms[i]); fmls.push_back(m.mk_not(m_asms[i])); m_core_activity[index]++; + indices.push_back(index); } fml = m.mk_or(fmls.size(), fmls.c_ptr()); TRACE("opt", tout << fml << "\n";); + m_hs.add_set(indices.size(), indices.c_ptr()); maxs->add_hard(fml); -#endif } @@ -1224,7 +1203,6 @@ namespace opt { rational r; expr_ref val(m); VERIFY(mdl->eval(e, val)); - std::cout << mk_pp(e, m) << " |-> " << val << "\n"; return a.is_numeral(val, r) && r.is_one(); } @@ -1684,10 +1662,12 @@ namespace opt { m_maxsmt = alloc(bcd2, s.get(), m); } else if (m_engine == symbol("hsmax")) { + //m_params.set_bool("pb.enable_simplex", true); ref<opt_solver> s0 = alloc(opt_solver, m, m_params, symbol()); s0->check_sat(0,0); maxsmt_solver_base* s2 = alloc(pbmax, s0.get(), m); // , s0->get_context()); s2->set_converter(s0->mc_ref().get()); + m_maxsmt = alloc(hsmax, s.get(), m, s2); } // NB: this is experimental one-round version of SLS diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index 854e15493..b4a1a6a8b 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -226,7 +226,7 @@ namespace qe { return alloc(sat_tactic, m); } - ~sat_tactic() { + virtual ~sat_tactic() { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 4799b8b9f..9ad787c2a 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -23,6 +23,7 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); m_macro_finder = p.macro_finder(); m_pull_nested_quantifiers = p.pull_nested_quantifiers(); + m_refine_inj_axiom = p.refine_inj_axioms(); } void preprocessor_params::updt_params(params_ref const & p) { diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 3dec59d29..a0d7e7823 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -14,6 +14,7 @@ def_module_params(module_name='smt', ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ingored if delay_units is false'), ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), + ('refine_inj_axioms', BOOL, True, 'refine injectivity axioms'), ('soft_timeout', UINT, 0, 'soft timeout (0 means no timeout)'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp index a45b53155..3b0ec8e5f 100644 --- a/src/smt/proto_model/datatype_factory.cpp +++ b/src/smt/proto_model/datatype_factory.cpp @@ -20,6 +20,7 @@ Revision History: #include"proto_model.h" #include"ast_pp.h" #include"ast_ll_pp.h" +#include"expr_functors.h" datatype_factory::datatype_factory(ast_manager & m, proto_model & md): struct_factory(m, m.mk_family_id("datatype"), md), @@ -47,8 +48,10 @@ expr * datatype_factory::get_some_value(sort * s) { */ expr * datatype_factory::get_last_fresh_value(sort * s) { expr * val = 0; - if (m_last_fresh_value.find(s, val)) + if (m_last_fresh_value.find(s, val)) { + TRACE("datatype_factory", tout << "cached fresh value: " << mk_pp(val, m_manager) << "\n";); return val; + } value_set * set = get_value_set(s); if (set->empty()) val = get_some_value(s); @@ -59,6 +62,17 @@ expr * datatype_factory::get_last_fresh_value(sort * s) { return val; } +bool datatype_factory::is_subterm_of_last_value(app* e) { + expr* last; + if (!m_last_fresh_value.find(m_manager.get_sort(e), last)) { + return false; + } + contains_app contains(m_manager, e); + bool result = contains(last); + TRACE("datatype_factory", tout << mk_pp(e, m_manager) << " in " << mk_pp(last, m_manager) << " " << result << "\n";); + return result; +} + /** \brief Create an almost fresh value. If s is recursive, then the result is not 0. It also updates m_last_fresh_value @@ -105,11 +119,18 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) { } } if (recursive || found_fresh_arg) { - expr * new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); + app * new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); SASSERT(!found_fresh_arg || !set->contains(new_value)); register_value(new_value); - if (m_util.is_recursive(s)) - m_last_fresh_value.insert(s, new_value); + if (m_util.is_recursive(s)) { + if (is_subterm_of_last_value(new_value)) { + new_value = static_cast<app*>(m_last_fresh_value.find(s)); + } + else { + m_last_fresh_value.insert(s, new_value); + } + } + TRACE("datatype_factory", tout << "almost fresh: " << mk_pp(new_value, m_manager) << "\n";); return new_value; } } @@ -170,8 +191,10 @@ expr * datatype_factory::get_fresh_value(sort * s) { // Approach 2) // For recursive datatypes. // search for constructor... + unsigned num_iterations = 0; if (m_util.is_recursive(s)) { while(true) { + ++num_iterations; TRACE("datatype_factory", tout << mk_pp(get_last_fresh_value(s), m_manager) << "\n";); ptr_vector<func_decl> const * constructors = m_util.get_datatype_constructors(s); ptr_vector<func_decl>::const_iterator it = constructors->begin(); @@ -181,12 +204,26 @@ expr * datatype_factory::get_fresh_value(sort * s) { expr_ref_vector args(m_manager); bool found_sibling = false; unsigned num = constructor->get_arity(); + TRACE("datatype_factory", tout << "checking constructor: " << constructor->get_name() << "\n";); for (unsigned i = 0; i < num; i++) { sort * s_arg = constructor->get_domain(i); + TRACE("datatype_factory", tout << mk_pp(s, m_manager) << " " + << mk_pp(s_arg, m_manager) << " are_siblings " + << m_util.are_siblings(s, s_arg) << " is_datatype " + << m_util.is_datatype(s_arg) << " found_sibling " + << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; - expr * maybe_new_arg = get_almost_fresh_value(s_arg); + expr * maybe_new_arg = 0; + if (num_iterations <= 1) { + maybe_new_arg = get_almost_fresh_value(s_arg); + } + else { + maybe_new_arg = get_fresh_value(s_arg); + } if (!maybe_new_arg) { + TRACE("datatype_factory", + tout << "no argument found for " << mk_pp(s_arg, m_manager) << "\n";); maybe_new_arg = m_model.get_some_value(s_arg); found_sibling = false; } @@ -202,6 +239,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { if (found_sibling) { expr_ref new_value(m_manager); new_value = m_manager.mk_app(constructor, args.size(), args.c_ptr()); + TRACE("datatype_factory", tout << "potential new value: " << mk_pp(new_value, m_manager) << "\n";); m_last_fresh_value.insert(s, new_value); if (!set->contains(new_value)) { register_value(new_value); diff --git a/src/smt/proto_model/datatype_factory.h b/src/smt/proto_model/datatype_factory.h index e8e1f2589..f70604ca8 100644 --- a/src/smt/proto_model/datatype_factory.h +++ b/src/smt/proto_model/datatype_factory.h @@ -29,6 +29,8 @@ class datatype_factory : public struct_factory { expr * get_last_fresh_value(sort * s); expr * get_almost_fresh_value(sort * s); + bool is_subterm_of_last_value(app* e); + public: datatype_factory(ast_manager & m, proto_model & md); virtual ~datatype_factory() {} diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 70287728e..26b35f0ed 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -247,6 +247,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { new_t = mk_some_interp_for(f); } else { + TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); is_ok = false; } } @@ -294,6 +295,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { // f is an uninterpreted function, there is no need to use m_simplifier.mk_app new_t = m_manager.mk_app(f, num_args, args.c_ptr()); trail.push_back(new_t); + TRACE("model_eval", tout << f->get_name() << " is uninterpreted\n";); is_ok = false; } } @@ -326,6 +328,7 @@ bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { todo.pop_back(); break; case AST_QUANTIFIER: + TRACE("model_eval", tout << "found quantifier\n" << mk_pp(a, m_manager) << "\n";); is_ok = false; // evaluator does not handle quantifiers. SASSERT(a != 0); eval_cache.insert(a, a); diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 8303d05ac..568e9fca9 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -396,7 +396,7 @@ namespace smt { // Support for evaluating expressions in the current model. proto_model * m_model; - obj_map<expr, expr *> m_eval_cache; + obj_map<expr, expr *> m_eval_cache[2]; expr_ref_vector m_eval_cache_range; ptr_vector<node> m_root_nodes; @@ -409,7 +409,8 @@ namespace smt { } void reset_eval_cache() { - m_eval_cache.reset(); + m_eval_cache[0].reset(); + m_eval_cache[1].reset(); m_eval_cache_range.reset(); } @@ -468,6 +469,7 @@ namespace smt { ~auf_solver() { flush_nodes(); + reset_eval_cache(); } void set_context(context * ctx) { @@ -547,7 +549,7 @@ namespace smt { for (obj_map<expr, unsigned>::iterator it = elems.begin(); it != elems.end(); it++) { expr * n = it->m_key; expr * n_val = eval(n, true); - if (!m_manager.is_value(n_val)) + if (!n_val || !m_manager.is_value(n_val)) to_delete.push_back(n); } for (ptr_vector<expr>::iterator it = to_delete.begin(); it != to_delete.end(); it++) { @@ -569,16 +571,19 @@ namespace smt { virtual expr * eval(expr * n, bool model_completion) { expr * r = 0; - if (m_eval_cache.find(n, r)) { + if (m_eval_cache[model_completion].find(n, r)) { return r; } expr_ref tmp(m_manager); - if (!m_model->eval(n, tmp, model_completion)) + if (!m_model->eval(n, tmp, model_completion)) { r = 0; - else + TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n-----> null\n";); + } + else { r = tmp; - TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); - m_eval_cache.insert(n, r); + TRACE("model_finder", tout << "eval\n" << mk_pp(n, m_manager) << "\n----->\n" << mk_pp(r, m_manager) << "\n";); + } + m_eval_cache[model_completion].insert(n, r); m_eval_cache_range.push_back(r); return r; } diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index cf22c3e3a..6c138fd57 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -102,6 +102,7 @@ namespace smt { if (th && th->build_models()) { if (r->get_th_var(th->get_id()) != null_theory_var) { proc = th->mk_value(r, *this); + SASSERT(proc); } else { TRACE("model_bug", tout << "creating fresh value for #" << r->get_owner_id() << "\n";); @@ -110,6 +111,7 @@ namespace smt { } else { proc = mk_model_value(r); + SASSERT(proc); } } SASSERT(proc); diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index 4ef1bd8e0..fc9138bf6 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -162,7 +162,7 @@ namespace smt { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n) { + virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) { return alloc(dl_value_proc, *this, n); } @@ -201,9 +201,8 @@ namespace smt { if(!m_reps.find(s, r) || !m_vals.find(s,v)) { SASSERT(!m_reps.contains(s)); sort* bv = b().mk_sort(64); - // TBD: filter these from model. - r = m().mk_fresh_func_decl("rep",1, &s,bv); - v = m().mk_fresh_func_decl("val",1, &bv,s); + r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, 0, 1, &s, bv); + v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, 0, 1, &bv, s); m_reps.insert(s, r); m_vals.insert(s, v); add_trail(r); diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index cb52f0e79..7570a73c5 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -471,7 +471,7 @@ namespace smt { break; } - if (c->k().is_one() && c->is_ge()) { + if (c->k().is_one() && c->is_ge() && !m_enable_simplex) { literal_vector& lits = get_lits(); lits.push_back(~lit); for (unsigned i = 0; i < c->size(); ++i) { @@ -480,7 +480,7 @@ namespace smt { ctx.mk_th_axiom(get_id(), lit, ~c->lit(i)); } ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); - // return true; + return true; } // maximal coefficient: diff --git a/src/test/main.cpp b/src/test/main.cpp index 4912916a1..3b0179944 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -219,6 +219,7 @@ int main(int argc, char ** argv) { TST(sorting_network); TST(theory_pb); TST(simplex); + //TST_ARGV(hs); } void initialize_mam() {} diff --git a/src/util/mpq_inf.h b/src/util/mpq_inf.h index 052183dd1..d7202a79a 100644 --- a/src/util/mpq_inf.h +++ b/src/util/mpq_inf.h @@ -248,7 +248,7 @@ public: void ceil(mpq_inf const & a, mpq & b) { if (m.is_int(a.first)) { // special cases for k - delta*epsilon where k is an integer - if (m.is_pos(a.first)) + if (m.is_pos(a.second)) m.add(a.first, mpq(1), b); // ceil(k + delta*epsilon) --> k+1 else m.set(b, a.first); @@ -276,6 +276,7 @@ public: out << to_string(a); } + mpq_manager<SYNCH>& mpq_manager() { return m; } }; typedef mpq_inf_manager<true> synch_mpq_inf_manager; diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index d0a79cec6..c4a640009 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -70,7 +70,9 @@ struct scoped_timer::imp { pthread_t m_thread_id; pthread_attr_t m_attributes; unsigned m_interval; + pthread_mutex_t m_mutex; pthread_cond_t m_condition_var; + struct timespec m_end_time; #elif defined(_LINUX_) || defined(_FREEBSD_) // Linux & FreeBSD timer_t m_timerid; @@ -93,35 +95,15 @@ struct scoped_timer::imp { static void * thread_func(void * arg) { scoped_timer::imp * st = static_cast<scoped_timer::imp*>(arg); - pthread_mutex_t mutex; - clock_serv_t host_clock; - struct timespec abstime; - mach_timespec_t now; - unsigned long long nano = static_cast<unsigned long long>(st->m_interval) * 1000000ull; + pthread_mutex_lock(&st->m_mutex); - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); - - if (pthread_mutex_init(&mutex, NULL) != 0) - throw default_exception("failed to initialize timer mutex"); - if (pthread_cond_init(&st->m_condition_var, NULL) != 0) - throw default_exception("failed to initialize timer condition variable"); - - abstime.tv_sec = nano / 1000000000ull; - abstime.tv_nsec = nano % 1000000000ull; - - pthread_mutex_lock(&mutex); - clock_get_time(host_clock, &now); - ADD_MACH_TIMESPEC(&abstime, &now); - int e = pthread_cond_timedwait(&st->m_condition_var, &mutex, &abstime); + int e = pthread_cond_timedwait(&st->m_condition_var, &st->m_mutex, &st->m_end_time); if (e != 0 && e != ETIMEDOUT) throw default_exception("failed to start timed wait"); st->m_eh->operator()(); - pthread_mutex_unlock(&mutex); - if (pthread_mutex_destroy(&mutex) != 0) - throw default_exception("failed to destroy pthread mutex"); - if (pthread_cond_destroy(&st->m_condition_var) != 0) - throw default_exception("failed to destroy pthread condition variable"); + pthread_mutex_unlock(&st->m_mutex); + return st; } #elif defined(_LINUX_) || defined(_FREEBSD_) @@ -150,6 +132,22 @@ struct scoped_timer::imp { m_interval = ms; if (pthread_attr_init(&m_attributes) != 0) throw default_exception("failed to initialize timer thread attributes"); + if (pthread_cond_init(&m_condition_var, NULL) != 0) + throw default_exception("failed to initialize timer condition variable"); + if (pthread_mutex_init(&m_mutex, NULL) != 0) + throw default_exception("failed to initialize timer mutex"); + + clock_serv_t host_clock; + mach_timespec_t now; + unsigned long long nano = static_cast<unsigned long long>(m_interval) * 1000000ull; + + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &host_clock); + m_end_time.tv_sec = nano / 1000000000ull; + m_end_time.tv_nsec = nano % 1000000000ull; + clock_get_time(host_clock, &now); + ADD_MACH_TIMESPEC(&m_end_time, &now); + + if (pthread_create(&m_thread_id, &m_attributes, &thread_func, this) != 0) throw default_exception("failed to start timer thread"); #elif defined(_LINUX_) || defined(_FREEBSD_) @@ -183,9 +181,25 @@ struct scoped_timer::imp { INVALID_HANDLE_VALUE); #elif defined(__APPLE__) && defined(__MACH__) // Mac OS X - pthread_cond_signal(&m_condition_var); // this is okay to fail + + // If the waiting-thread is not up and waiting yet, + // we can make sure that it finishes quickly by + // setting the end-time to zero. + m_end_time.tv_sec = 0; + m_end_time.tv_nsec = 0; + + // Otherwise it's already up and waiting, and + // we can send a signal on m_condition_var: + pthread_mutex_lock(&m_mutex); + pthread_cond_signal(&m_condition_var); + pthread_mutex_unlock(&m_mutex); + if (pthread_join(m_thread_id, NULL) != 0) throw default_exception("failed to join thread"); + if (pthread_mutex_destroy(&m_mutex) != 0) + throw default_exception("failed to destroy pthread mutex"); + if (pthread_cond_destroy(&m_condition_var) != 0) + throw default_exception("failed to destroy pthread condition variable"); if (pthread_attr_destroy(&m_attributes) != 0) throw default_exception("failed to destroy pthread attributes object"); #elif defined(_LINUX_) || defined(_FREEBSD_)