From ba193a59b18328a9ae318ab052e24c522b6d61d6 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 9 Feb 2014 16:04:02 -0800 Subject: [PATCH 01/54] some interpolation fixes, plus some options to remove features for testing in duality --- src/duality/duality.h | 0 src/duality/duality_rpfp.cpp | 0 src/duality/duality_solver.cpp | 15 +++- src/duality/duality_wrapper.cpp | 0 src/interp/iz3mgr.cpp | 9 +- src/interp/iz3mgr.h | 0 src/interp/iz3proof_itp.cpp | 64 +++++++++++++- src/interp/iz3translate.cpp | 102 +++++++++++++++++++++-- src/muz/duality/duality_dl_interface.cpp | 0 9 files changed, 176 insertions(+), 14 deletions(-) mode change 100644 => 100755 src/duality/duality.h mode change 100644 => 100755 src/duality/duality_rpfp.cpp mode change 100644 => 100755 src/duality/duality_solver.cpp mode change 100644 => 100755 src/duality/duality_wrapper.cpp mode change 100644 => 100755 src/interp/iz3mgr.cpp mode change 100644 => 100755 src/interp/iz3mgr.h mode change 100644 => 100755 src/interp/iz3proof_itp.cpp mode change 100644 => 100755 src/muz/duality/duality_dl_interface.cpp diff --git a/src/duality/duality.h b/src/duality/duality.h old mode 100644 new mode 100755 diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp old mode 100644 new mode 100755 diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp old mode 100644 new mode 100755 index 91532960b..6d39a4fe2 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -44,12 +44,17 @@ Revision History: // #define TOP_DOWN // #define EFFORT_BOUNDED_STRAT #define SKIP_UNDERAPPROX_NODES -#define USE_RPFP_CLONE // #define KEEP_EXPANSIONS // #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK + +#define USE_RPFP_CLONE #define USE_NEW_GEN_CANDS +//#define NO_PROPAGATE +//#define NO_GENERALIZE +//#define NO_DECISIONS + namespace Duality { // TODO: must be a better place for this... @@ -1933,11 +1938,15 @@ namespace Duality { for(unsigned i = 0; i < expansions.size(); i++){ Node *node = expansions[i]; tree->SolveSingleNode(top,node); +#ifdef NO_GENERALIZE + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); +#else if(expansions.size() == 1 && NodeTooComplicated(node)) SimplifyNode(node); else node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); Generalize(node); +#endif if(RecordUpdate(node)) update_count++; else @@ -1977,7 +1986,9 @@ namespace Duality { if(stack.size() == 1)break; if(prev_level_used){ Node *node = stack.back().expansions[0]; +#ifndef NO_PROPAGATE if(!Propagate(node)) break; +#endif if(!RecordUpdate(node)) break; // shouldn't happen! RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list propagated = true; @@ -2001,9 +2012,11 @@ namespace Duality { was_sat = true; tree->Push(); std::vector &expansions = stack.back().expansions; +#ifndef NO_DECISIONS for(unsigned i = 0; i < expansions.size(); i++){ tree->FixCurrentState(expansions[i]->Outgoing); } +#endif #if 0 if(tree->slvr().check() == unsat) throw "help!"; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp old mode 100644 new mode 100755 diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp old mode 100644 new mode 100755 index 994d10e06..329fcb305 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -684,10 +684,13 @@ void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q, bool round_off){ throw "not an inequality"; } } - Qrhs = make(Times,c,Qrhs); - bool pstrict = op(P) == Lt, strict = pstrict || qstrict; - if(pstrict && qstrict && round_off) + bool pstrict = op(P) == Lt; + if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ Qrhs = make(Sub,Qrhs,make_int(rational(1))); + qstrict = false; + } + Qrhs = make(Times,c,Qrhs); + bool strict = pstrict || qstrict; if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h old mode 100644 new mode 100755 diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp old mode 100644 new mode 100755 index 8b78a7135..2d79e8151 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -677,8 +677,11 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Bproves, ast &ineq){ - if(pf == pl) + if(pf == pl){ + if(sym(ineq) == normal) + return my_implies(Bproves,ineq); return my_implies(Bproves,simplify_ineq(ineq)); + } if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ ast Aproves = mk_true(); @@ -829,6 +832,8 @@ class iz3proof_itp_impl : public iz3proof_itp { ast equa = sep_cond(args[0],cond); if(is_equivrel_chain(equa)) return my_implies(cond,reverse_chain(equa)); + if(is_negation_chain(equa)) + return commute_negation_chain(equa); throw cannot_simplify(); } @@ -1443,6 +1448,50 @@ class iz3proof_itp_impl : public iz3proof_itp { return is_negation_chain(rest); } + ast commute_negation_chain(const ast &chain){ + if(is_true(chain)) + return chain; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + if(is_true(rest)){ + ast old = rewrite_rhs(last); + if(!(op(old) == Not)) + throw "bad negative equality chain"; + ast equ = arg(old,0); + if(!is_equivrel(equ)) + throw "bad negative equality chain"; + last = rewrite_update_rhs(last,top_pos,make(Not,make(op(equ),arg(equ,1),arg(equ,0))),make(True)); + return chain_cons(rest,last); + } + ast pos = rewrite_pos(last); + if(pos == top_pos) + throw "bad negative equality chain"; + int idx = pos_arg(pos); + if(idx != 0) + throw "bad negative equality chain"; + pos = arg(pos,1); + if(pos == top_pos){ + ast lhs = rewrite_lhs(last); + ast rhs = rewrite_rhs(last); + if(op(lhs) != Equal || op(rhs) != Equal) + throw "bad negative equality chain"; + last = make_rewrite(rewrite_side(last),rewrite_pos(last),rewrite_cond(last), + make(Iff,make(Equal,arg(lhs,1),arg(lhs,0)),make(Equal,arg(rhs,1),arg(rhs,0)))); + } + else { + idx = pos_arg(pos); + if(idx == 0) + idx = 1; + else if(idx == 1) + idx = 0; + else + throw "bad negative equality chain"; + pos = pos_add(0,pos_add(idx,arg(pos,1))); + last = make_rewrite(rewrite_side(last),pos,rewrite_cond(last),rewrite_equ(last)); + } + return chain_cons(commute_negation_chain(rest),last); + } + // split a rewrite chain into head and tail at last top-level rewrite ast get_head_chain(const ast &chain, ast &tail, bool is_not = true){ ast last = chain_last(chain); @@ -2240,10 +2289,19 @@ class iz3proof_itp_impl : public iz3proof_itp { throw proof_error(); } } - Qrhs = make(Times,c,Qrhs); +#if 0 bool pstrict = op(P) == Lt, strict = pstrict || qstrict; if(pstrict && qstrict && round_off) Qrhs = make(Sub,Qrhs,make_int(rational(1))); +#else + bool pstrict = op(P) == Lt; + if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ + Qrhs = make(Sub,Qrhs,make_int(rational(1))); + qstrict = false; + } + Qrhs = make(Times,c,Qrhs); + bool strict = pstrict || qstrict; +#endif if(strict) P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); else @@ -2427,7 +2485,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return e; // this term occurs in range, so it's O.K. if(is_array_type(get_type(e))) - throw "help!"; + std::cerr << "WARNING: array quantifier\n"; // choose a frame for the constraint that is close to range int frame = pv->range_near(pv->ast_scope(e),rng); diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index abddb4bda..bba7b4d0d 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -181,6 +181,13 @@ public: get_Z3_lits(con, lits); iproof->make_axiom(lits); } + else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST + && get_locality_rec(prem(proof,1)) == INT_MAX){ + std::vector lits; + ast con = conc(proof); + get_Z3_lits(con, lits); + iproof->make_axiom(lits); + } else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ @@ -1264,6 +1271,84 @@ public: return make(Plus,args); } + + ast replace_summands_with_fresh_vars(const ast &t, hash_map &map){ + if(op(t) == Plus){ + int nargs = num_args(t); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = replace_summands_with_fresh_vars(arg(t,i),map); + return make(Plus,args); + } + if(op(t) == Times) + return make(Times,arg(t,0),replace_summands_with_fresh_vars(arg(t,1),map)); + if(map.find(t) == map.end()) + map[t] = mk_fresh_constant("@s",get_type(t)); + return map[t]; + } + + ast painfully_normalize_ineq(const ast &ineq, hash_map &map){ + ast res = normalize_inequality(ineq); + ast lhs = arg(res,0); + lhs = replace_summands_with_fresh_vars(lhs,map); + res = make(op(res),SortSum(lhs),arg(res,1)); + return res; + } + + Iproof::node painfully_reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ + int nprems = prems.size(); + std::vector pcons(nprems),npcons(nprems); + hash_map pcon_to_pf, npcon_to_pcon, pain_map; + for(int i = 0; i < nprems; i++){ + pcons[i] = conc(prems[i]); + npcons[i] = painfully_normalize_ineq(pcons[i],pain_map); + pcon_to_pf[npcons[i]] = pfs[i]; + npcon_to_pcon[npcons[i]] = pcons[i]; + } + // ast leq = make(Leq,arg(con,0),arg(con,1)); + ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); + pcons.push_back(mk_not(con)); + npcons.push_back(ncon); + // ast assumps = make(And,pcons); + ast new_proof; + if(is_sat(npcons,new_proof)) + throw "Proof error!"; + pfrule dk = pr(new_proof); + int nnp = num_prems(new_proof); + std::vector my_prems; + std::vector farkas_coeffs, my_pcons; + + if(dk == PR_TH_LEMMA + && get_theory_lemma_theory(new_proof) == ArithTheory + && get_theory_lemma_kind(new_proof) == FarkasKind) + get_farkas_coeffs(new_proof,farkas_coeffs); + else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ + for(int i = 0; i < nprems; i++) + farkas_coeffs.push_back(make_int(rational(1))); + } + else + throw "cannot reconstruct farkas proof"; + + for(int i = 0; i < nnp; i++){ + ast p = conc(prem(new_proof,i)); + p = really_normalize_ineq(p); + if(pcon_to_pf.find(p) != pcon_to_pf.end()){ + my_prems.push_back(pcon_to_pf[p]); + my_pcons.push_back(npcon_to_pcon[p]); + } + else if(p == ncon){ + my_prems.push_back(iproof->make_hypothesis(mk_not(con))); + my_pcons.push_back(mk_not(con)); + } + else + throw "cannot reconstruct farkas proof"; + } + Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); + return res; + } + + + ast really_normalize_ineq(const ast &ineq){ ast res = normalize_inequality(ineq); res = make(op(res),SortSum(arg(res,0)),arg(res,1)); @@ -1302,7 +1387,7 @@ public: farkas_coeffs.push_back(make_int(rational(1))); } else - throw "cannot reconstruct farkas proof"; + return painfully_reconstruct_farkas(prems,pfs,con); for(int i = 0; i < nnp; i++){ ast p = conc(prem(new_proof,i)); @@ -1445,9 +1530,11 @@ public: lits.push_back(from_ast(con)); // pattern match some idioms - if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && pr(prem(proof,1)) == PR_REWRITE ) { - res = iproof->make_axiom(lits); - return res; + if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST){ + if(get_locality_rec(prem(proof,1)) == INT_MAX) { + res = iproof->make_axiom(lits); + return res; + } } if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or){ Iproof::node clause = translate_main(prem(proof,0),true); @@ -1620,9 +1707,10 @@ public: break; case ArrayTheory: {// nothing fancy for this ast store_array; - if(!get_store_array(con,store_array)) - throw unsupported(); - res = iproof->make_axiom(lits,ast_scope(store_array)); + if(get_store_array(con,store_array)) + res = iproof->make_axiom(lits,ast_scope(store_array)); + else + res = iproof->make_axiom(lits); // for array extensionality axiom break; } default: diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp old mode 100644 new mode 100755 From e860c6556714994a84669e6bc1240bc4e799957c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 13 Feb 2014 19:33:51 +0000 Subject: [PATCH 02/54] bugfix for sign computation in floating-point FMA Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/fpa2bv_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 144925164..2d1d6705f 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -1368,12 +1368,12 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar not_e_sgn = m_bv_util.mk_bv_not(e_sgn); not_f_sgn = m_bv_util.mk_bv_not(f_sgn); not_sign_bv = m_bv_util.mk_bv_not(sign_bv); - res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, e_sgn, sign_bv); + res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_e_sgn, f_sgn, sign_bv); res_sgn_c2 = m.mk_app(bvfid, OP_BAND, e_sgn, not_f_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, e_sgn, f_sgn); expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); - + sticky_raw = m_bv_util.mk_extract(sbits-5, 0, sig_abs); sticky = m_bv_util.mk_zero_extend(sbits+3, m.mk_app(bvfid, OP_BREDOR, sticky_raw.get())); dbg_decouple("fpa2bv_fma_add_sum_sticky", sticky); From 88ff20a0fb7a858a3b5516fcb7bf15c7f832cf27 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 12 Feb 2014 14:48:39 -0800 Subject: [PATCH 03/54] fixed multiple interpolation bugs --- src/interp/iz3proof_itp.cpp | 93 +++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 2d79e8151..8662f34d5 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -745,6 +745,26 @@ class iz3proof_itp_impl : public iz3proof_itp { return res; } + ast unmixed_eq2ineq(const ast &lhs, const ast &rhs, opr comp_op, const ast &equa, ast &cond){ + ast ineqs= chain_ineqs(comp_op,LitA,equa,lhs,rhs); // chain must be from lhs to rhs + cond = my_and(cond,chain_conditions(LitA,equa)); + ast Bconds = z3_simplify(chain_conditions(LitB,equa)); + if(is_true(Bconds) && op(ineqs) != And) + return my_implies(cond,ineqs); + throw cannot_simplify(); + } + + ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ + if(is_true(equa)) + return itp; + std::vector args(3); + args[0] = itp; + args[1] = make_int("1"); + ast ineq = make(Leq,make_int(rational(0)),make_int(rational(0))); + args[2] = make_normal(ineq,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); + return simplify_sum(args); + } + ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,1)){ ast cond = mk_true(); @@ -752,16 +772,17 @@ class iz3proof_itp_impl : public iz3proof_itp { if(is_equivrel_chain(equa)){ ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); - if(lhst != LitMixed && rhst != LitMixed){ - ast ineqs= chain_ineqs(op(arg(neg_equality,0)),LitA,equa,lhs,rhs); // chain must be from lhs to rhs - cond = my_and(cond,chain_conditions(LitA,equa)); - ast Bconds = z3_simplify(chain_conditions(LitB,equa)); - if(is_true(Bconds) && op(ineqs) != And) - return my_implies(cond,ineqs); - } + if(lhst != LitMixed && rhst != LitMixed) + return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); else { - ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); - return make_normal(itp,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); + ast left, left_term, middle, right_term, right; + left = get_left_movers(equa,lhs,middle,left_term); + middle = get_right_movers(middle,rhs,right,right_term); + ast itp = unmixed_eq2ineq(left_term, right_term, op(arg(neg_equality,0)), middle, cond); + // itp = my_implies(cond,itp); + itp = add_mixed_eq2ineq(lhs, left_term, left, itp); + itp = add_mixed_eq2ineq(right_term, rhs, right, itp); + return itp; } } } @@ -907,7 +928,9 @@ class iz3proof_itp_impl : public iz3proof_itp { get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); ast itp; if(is_rewrite_side(LitA,head)){ - itp = ineq1; + itp = make(Leq,make_int("0"),make_int("0")); + linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form + //itp = ineq1; ast mc = z3_simplify(chain_side_proves(LitB,pref)); Bproves = my_and(Bproves,mc); } @@ -1508,6 +1531,43 @@ class iz3proof_itp_impl : public iz3proof_itp { return head; } + // split a rewrite chain into head and tail at last non-mixed term + ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ + if(is_true(chain) || get_term_type(rhs) != LitMixed){ + mid = rhs; + tail = mk_true(); + return chain; + } + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); + ast res = get_right_movers(rest,mm,tail,mid); + tail = chain_cons(tail,last); + return chain; + } + + // split a rewrite chain into head and tail at first non-mixed term + ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ + if(is_true(chain)){ + mid = lhs; + return ast(); + } + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast res = get_left_movers(rest,lhs,tail,mid); + if(res.null()){ + mid = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); + if(get_term_type(mid) != LitMixed){ + tail = mk_true(); + return chain; + } + return ast(); + } + tail = chain_cons(tail,last); + return res; + } + + struct cannot_split {}; /** Split a chain of rewrites two chains, operating on positions 0 and 1. @@ -2320,8 +2380,14 @@ class iz3proof_itp_impl : public iz3proof_itp { itp = mk_true(); break; default: { // mixed equality - if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed) - std::cerr << "WARNING: mixed term in leq2eq\n"; + if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ + // std::cerr << "WARNING: mixed term in leq2eq\n"; + std::vector lits; + lits.push_back(con); + lits.push_back(make(Not,xleqy)); + lits.push_back(make(Not,yleqx)); + return make_axiom(lits); + } std::vector conjs; conjs.resize(3); conjs[0] = mk_not(con); conjs[1] = xleqy; @@ -2456,7 +2522,8 @@ class iz3proof_itp_impl : public iz3proof_itp { frng = srng; // this term will be localized } else if(o == Plus || o == Times){ // don't want bound variables inside arith ops - frng = erng; // this term will be localized + // std::cout << "WARNING: non-local arithmetic\n"; + // frng = erng; // this term will be localized } std::vector largs(nargs); std::vector eqs; From cb3dc63e68ff6db81e3bbee5ad07791bf59eead6 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Feb 2014 14:03:54 -0800 Subject: [PATCH 04/54] some interpolation fixes; make duality a bit more persistent in checking interpolant --- src/duality/duality_rpfp.cpp | 8 +-- src/interp/iz3mgr.cpp | 14 ++++++ src/interp/iz3proof_itp.cpp | 96 +++++++++++++++++++++++++----------- 3 files changed, 87 insertions(+), 31 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index ad89c1314..9813bce6a 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2703,10 +2703,12 @@ namespace Duality { const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) s.add(theory[i]); - if(s.check(lits.size(),&lits[0]) != unsat) - throw "should be unsat"; + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if(s.check(lits.size(),&lits[0]) == unsat) + goto is_unsat; + throw "should be unsat"; } - + is_unsat: for(unsigned i = 0; i < conjuncts.size(); ){ std::swap(conjuncts[i],conjuncts.back()); std::swap(lits[i],lits.back()); diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 329fcb305..0a93d0e73 100755 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -240,6 +240,9 @@ iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ void iz3mgr::show(ast t){ + if(t.null()){ + std::cout << "(null)" << std::endl; + } params_ref p; p.set_bool("flat_assoc",false); std::cout << mk_pp(t.raw(), m(), p) << std::endl; @@ -875,3 +878,14 @@ iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ std::vector bvs; bvs.push_back(var); return make_quant(quantifier,bvs,e); } + +#if 0 +void iz3mgr::get_bound_substitutes(stl_ext::hash_map &memo, const ast &e, const ast &var, std::vector &substs){ + std::pair foo(e,false); + std::pair::iterator,bool> bar = memo.insert(foo); + if(bar.second){ + if(op(e) == + } + +} +#endif diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 8662f34d5..cf86e9914 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -572,18 +572,36 @@ class iz3proof_itp_impl : public iz3proof_itp { return is_ineq(ineq); } + ast destruct_cond_ineq(const ast &ineq, ast &Aproves, ast &Bproves){ + ast res = ineq; + opr o = op(res); + if(o == And){ + Aproves = my_and(Aproves,arg(res,0)); + res = arg(res,1); + o = op(res); + } + if(o == Implies){ + Bproves = my_and(Bproves,arg(res,0)); + res = arg(res,1); + } + return res; + } + ast simplify_sum(std::vector &args){ ast Aproves = mk_true(), Bproves = mk_true(); - ast ineq = args[0]; + ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); if(!is_normal_ineq(ineq)) throw cannot_simplify(); sum_cond_ineq(ineq,args[1],args[2],Aproves,Bproves); return my_and(Aproves,my_implies(Bproves,ineq)); } ast simplify_rotate_sum(const ast &pl, const ast &pf){ - ast cond = mk_true(); + ast Aproves = mk_true(), Bproves = mk_true(); ast ineq = make(Leq,make_int("0"),make_int("0")); - return rotate_sum_rec(pl,pf,cond,ineq); + ineq = rotate_sum_rec(pl,pf,Aproves,Bproves,ineq); + if(is_true(Aproves) && is_true(Bproves)) + return ineq; + return my_and(Aproves,my_implies(Bproves,ineq)); } bool is_rewrite_chain(const ast &chain){ @@ -616,7 +634,11 @@ class iz3proof_itp_impl : public iz3proof_itp { void sum_cond_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ opr o = op(ineq2); - if(o == Implies){ + if(o == And){ + sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); + Aproves = my_and(Aproves,arg(ineq2,0)); + } + else if(o == Implies){ sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); Bproves = my_and(Bproves,arg(ineq2,0)); } @@ -676,26 +698,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); } - ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Bproves, ast &ineq){ + ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Aproves, ast &Bproves, ast &ineq){ if(pf == pl){ if(sym(ineq) == normal) - return my_implies(Bproves,ineq); - return my_implies(Bproves,simplify_ineq(ineq)); + return ineq; + return simplify_ineq(ineq); } if(op(pf) == Uninterpreted && sym(pf) == sum){ if(arg(pf,2) == pl){ - ast Aproves = mk_true(); sum_cond_ineq(ineq,make_int("1"),arg(pf,0),Aproves,Bproves); - if(!is_true(Aproves)) - throw "help!"; ineq = idiv_ineq(ineq,arg(pf,1)); - return my_implies(Bproves,ineq); + return ineq; } - ast Aproves = mk_true(); sum_cond_ineq(ineq,arg(pf,1),arg(pf,2),Aproves,Bproves); - if(!is_true(Aproves)) - throw "help!"; - return rotate_sum_rec(pl,arg(pf,0),Bproves,ineq); + return rotate_sum_rec(pl,arg(pf,0),Aproves,Bproves,ineq); } throw cannot_simplify(); } @@ -751,7 +767,9 @@ class iz3proof_itp_impl : public iz3proof_itp { ast Bconds = z3_simplify(chain_conditions(LitB,equa)); if(is_true(Bconds) && op(ineqs) != And) return my_implies(cond,ineqs); - throw cannot_simplify(); + if(op(ineqs) != And) + return my_and(Bconds,my_implies(cond,ineqs)); + throw "help!"; } ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ @@ -786,7 +804,7 @@ class iz3proof_itp_impl : public iz3proof_itp { } } } - throw cannot_simplify(); + throw "help!"; } void reverse_modpon(std::vector &args){ @@ -972,7 +990,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast simplify_modpon(const std::vector &args){ ast Aproves = mk_true(), Bproves = mk_true(); ast chain = simplify_modpon_fwd(args,Bproves); - ast Q2 = sep_cond(args[2],Bproves); + ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); ast interp; if(is_normal_ineq(Q2)){ // inequalities are special ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); @@ -1543,13 +1561,17 @@ class iz3proof_itp_impl : public iz3proof_itp { ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); ast res = get_right_movers(rest,mm,tail,mid); tail = chain_cons(tail,last); - return chain; + return res; } // split a rewrite chain into head and tail at first non-mixed term ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ if(is_true(chain)){ - mid = lhs; + mid = lhs; + if(get_term_type(lhs) != LitMixed){ + tail = mk_true(); + return chain; + } return ast(); } ast last = chain_last(chain); @@ -1770,11 +1792,13 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ + LitType lhst = get_term_type(lhs); LitType rhst = get_term_type(rhs); - if(rhst != LitMixed || ast_id(lhs) < ast_id(rhs)) + if(lhst == LitMixed && (rhst != LitMixed || ast_id(lhs) < ast_id(rhs))) return make_normal_step(lhs,rhs,proof); - else + if(rhst == LitMixed && (lhst != LitMixed || ast_id(rhs) < ast_id(lhs))) return make_normal_step(rhs,lhs,reverse_chain(proof)); + throw "help!"; } ast chain_side_proves(LitType side, const ast &chain){ @@ -1794,8 +1818,10 @@ class iz3proof_itp_impl : public iz3proof_itp { ast lhs2 = normal_lhs(f2); int id1 = ast_id(lhs1); int id2 = ast_id(lhs2); - if(id1 < id2) return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); - if(id2 < id1) return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); + if(id1 < id2) + return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); + if(id2 < id1) + return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); ast rhs1 = normal_rhs(f1); ast rhs2 = normal_rhs(f2); LitType t1 = get_term_type(rhs1); @@ -1821,9 +1847,13 @@ class iz3proof_itp_impl : public iz3proof_itp { Aproves = my_and(Aproves,mcB); ast rep = apply_rewrite_chain(rhs1,Aproof); new_proof = concat_rewrite_chain(pf1,Aproof); - new_normal = make_normal_step(rhs1,rep,new_proof); + new_normal = make_normal_step(lhs1,rep,new_proof); + ast A_normal = make_normal_step(rhs1,rep,Aproof); + ast res = cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); + res = merge_normal_chains_rec(res,cons_normal(A_normal,make(True)),trans,Aproves,Bproves); + return res; } - else if(t1 == LitA && t2 == LitB) + else if(t1 == LitB && t2 == LitA) return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); else if(t1 == LitA) { ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); @@ -1845,17 +1875,20 @@ class iz3proof_itp_impl : public iz3proof_itp { return chain; ast f = normal_first(chain); ast r = normal_rest(chain); + r = trans_normal_chain(r,trans); ast rhs = normal_rhs(f); hash_map::iterator it = trans.find(rhs); ast new_normal; - if(it != trans.end()){ + if(it != trans.end() && get_term_type(normal_lhs(f)) == LitMixed){ const ast &f2 = it->second; ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); new_normal = make_normal_step(normal_lhs(f),normal_rhs(f2),pf); } else new_normal = f; - return cons_normal(new_normal,trans_normal_chain(r,trans)); + if(get_term_type(normal_lhs(f)) == LitMixed) + trans[normal_lhs(f)] = new_normal; + return cons_normal(new_normal,r); } ast merge_normal_chains(const ast &chain1, const ast &chain2, ast &Aproves, ast &Bproves){ @@ -2525,6 +2558,13 @@ class iz3proof_itp_impl : public iz3proof_itp { // std::cout << "WARNING: non-local arithmetic\n"; // frng = erng; // this term will be localized } + else if(o == Select){ // treat the array term like a function symbol + prover::range srng = pv->ast_scope(arg(e,0)); + if(!(srng.lo > srng.hi) && pv->ranges_intersect(srng,rng)) // localize to desired range if possible + frng = pv->range_glb(srng,rng); + else + frng = srng; // this term will be localized + } std::vector largs(nargs); std::vector eqs; std::vector pfs; From 928d419138ebb463d613b9f164078d52f359f9b9 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 17 Feb 2014 12:15:11 -0800 Subject: [PATCH 05/54] duality fixes --- src/duality/duality.h | 22 +++++++++++++----- src/duality/duality_rpfp.cpp | 41 +++++++++++++++++++++++++--------- src/duality/duality_solver.cpp | 10 ++------- src/interp/iz3translate.cpp | 2 ++ 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 8c469b9e4..aacc04f24 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1184,7 +1184,13 @@ namespace Duality { hash_map EdgeCloneMap; std::vector alit_stack; std::vector alit_stack_sizes; - hash_map > edge_solvers; + + // to let us use one solver per edge + struct edge_solver { + hash_map AssumptionLits; + uptr slvr; + }; + hash_map edge_solvers; #ifdef LIMIT_STACK_WEIGHT struct weight_counter { @@ -1236,19 +1242,23 @@ namespace Duality { void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - LogicSolver *SolverForEdge(Edge *edge, bool models); + edge_solver &SolverForEdge(Edge *edge, bool models); public: struct scoped_solver_for_edge { - LogicSolver *orig_ls; + solver *orig_slvr; RPFP_caching *rpfp; + edge_solver *es; scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false){ rpfp = _rpfp; - orig_ls = rpfp->ls; - rpfp->ls = rpfp->SolverForEdge(edge,models); + orig_slvr = rpfp->ls->slvr; + es = &(rpfp->SolverForEdge(edge,models)); + rpfp->ls->slvr = es->slvr.get(); + rpfp->AssumptionLits.swap(es->AssumptionLits); } ~scoped_solver_for_edge(){ - rpfp->ls = orig_ls; + rpfp->ls->slvr = orig_slvr; + rpfp->AssumptionLits.swap(es->AssumptionLits); } }; diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 9813bce6a..173959567 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2741,8 +2741,20 @@ namespace Duality { // verify check_result res = CheckCore(lits,full_core); - if(res != unsat) + if(res != unsat){ + // add the axioms in the off chance they are useful + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + GetAssumptionLits(theory[i],assumps); + lits = assumps; + std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); + + for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! + if((res = CheckCore(lits,full_core)) == unsat) + goto is_unsat; throw "should be unsat"; + } + is_unsat: FilterCore(core,full_core); std::vector dummy; @@ -2883,13 +2895,14 @@ namespace Duality { timer_stop("Generalize"); } - RPFP::LogicSolver *RPFP_caching::SolverForEdge(Edge *edge, bool models){ - uptr &p = edge_solvers[edge]; + RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models){ + edge_solver &es = edge_solvers[edge]; + uptr &p = es.slvr; if(!p.get()){ scoped_no_proof no_proofs_please(ctx.m()); // no proofs - p.set(new iZ3LogicSolver(ctx,models)); // no models + p.set(new solver(ctx,true,models)); // no models } - return p.get(); + return es; } @@ -3356,6 +3369,8 @@ namespace Duality { } } + bool some_labels = false; + // create the edges for(unsigned i = 0; i < clauses.size(); i++){ @@ -3391,17 +3406,23 @@ namespace Duality { Term labeled = body; std::vector lbls; // TODO: throw this away for now body = RemoveLabels(body,lbls); + if(!eq(labeled,body)) + some_labels = true; // remember if there are labels, as we then can't do qe_lite // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. body = body.simplify(); #ifdef USE_QE_LITE std::set idxs; - for(unsigned j = 0; j < Indparams.size(); j++) - if(Indparams[j].is_var()) - idxs.insert(Indparams[j].get_index_value()); - body = body.qe_lite(idxs,false); + if(!some_labels){ // can't do qe_lite if we have to reconstruct labels + for(unsigned j = 0; j < Indparams.size(); j++) + if(Indparams[j].is_var()) + idxs.insert(Indparams[j].get_index_value()); + body = body.qe_lite(idxs,false); + } hash_map > sb_memo; body = SubstBoundRec(sb_memo,substs[i],0,body); + if(some_labels) + labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); for(unsigned j = 0; j < Indparams.size(); j++) Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); #endif @@ -3663,7 +3684,7 @@ namespace Duality { for(unsigned j = 0; j < outs.size(); j++) for(unsigned k = 0; k < outs[j]->Children.size(); k++) chs.push_back(outs[j]->Children[k]); - Edge *fedge = CreateEdge(node,tr,chs); + CreateEdge(node,tr,chs); for(unsigned j = 0; j < outs.size(); j++) edges_to_delete.insert(outs[j]); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 6d39a4fe2..e3a294550 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -127,13 +127,11 @@ namespace Duality { { scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE - clone_ls = new RPFP::iZ3LogicSolver(ctx, false); // no models needed for this one - clone_rpfp = new RPFP_caching(clone_ls); + clone_rpfp = new RPFP_caching(rpfp->ls); clone_rpfp->Clone(rpfp); #endif #ifdef USE_NEW_GEN_CANDS - gen_cands_ls = new RPFP::iZ3LogicSolver(ctx); - gen_cands_rpfp = new RPFP_caching(gen_cands_ls); + gen_cands_rpfp = new RPFP_caching(rpfp->ls); gen_cands_rpfp->Clone(rpfp); #endif } @@ -142,20 +140,16 @@ namespace Duality { ~Duality(){ #ifdef USE_RPFP_CLONE delete clone_rpfp; - delete clone_ls; #endif #ifdef USE_NEW_GEN_CANDS delete gen_cands_rpfp; - delete gen_cands_ls; #endif } #ifdef USE_RPFP_CLONE - RPFP::LogicSolver *clone_ls; RPFP_caching *clone_rpfp; #endif #ifdef USE_NEW_GEN_CANDS - RPFP::LogicSolver *gen_cands_ls; RPFP_caching *gen_cands_rpfp; #endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index bba7b4d0d..ef0c6f5ec 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -181,6 +181,7 @@ public: get_Z3_lits(con, lits); iproof->make_axiom(lits); } +#ifdef LOCALIZATION_KLUDGE else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST && get_locality_rec(prem(proof,1)) == INT_MAX){ std::vector lits; @@ -188,6 +189,7 @@ public: get_Z3_lits(con, lits); iproof->make_axiom(lits); } +#endif else { unsigned nprems = num_prems(proof); for(unsigned i = 0; i < nprems; i++){ From 76fb66314b12d38c423c77001b05c9b1d7dbfe53 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Feb 2014 13:56:16 -0800 Subject: [PATCH 06/54] interpolation fix for commutativity --- src/interp/iz3translate.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index ef0c6f5ec..fd4cbb32b 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1547,12 +1547,20 @@ public: if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) std::cout << "foo!\n"; +#if 0 if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ Iproof::node clause = translate_main(prem(proof,0),true); res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. return res; } + if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,0)) == PR_COMMUTATIVITY){ + Iproof::node clause = translate_main(prem(proof,1),true); + res = make(commute,clause,conc(prem(proof,1))); // HACK -- we depend on Iproof::node being same as ast. + return res; + } +#endif + if(dk == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ try { res = CombineEqPropagate(proof); @@ -1736,6 +1744,12 @@ public: res = args[0]; break; } + case PR_COMMUTATIVITY: { + ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); + ast pf = iproof->make_reflexivity(comm_equiv); + res = make(commute,pf,comm_equiv); + break; + } default: assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); From c9cf658e7ccc19f470789e923e10f84ffa7d54f2 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Feb 2014 13:56:55 -0800 Subject: [PATCH 07/54] interpolation fix for commutativity --- src/interp/iz3proof_itp.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index cf86e9914..fac7849ec 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2146,8 +2146,14 @@ class iz3proof_itp_impl : public iz3proof_itp { /** Make a Reflexivity node. This rule produces |- x = x */ virtual node make_reflexivity(ast con){ - throw proof_error(); -} + if(get_term_type(con) == LitA) + return mk_false(); + if(get_term_type(con) == LitB) + return mk_true(); + ast itp = make(And,make(contra,no_proof,mk_false()), + make(contra,mk_true(),mk_not(con))); + return itp; + } /** Make a Symmetry node. This takes a derivation of |- x = y and produces | y = x. Ditto for ~(x=y) */ From 65c54b87d09495c711f9a0b140c875f814b9ec07 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Feb 2014 13:57:27 -0800 Subject: [PATCH 08/54] duality fix --- src/duality/duality.h | 6 +++--- src/duality/duality_rpfp.cpp | 8 +++++++- src/duality/duality_solver.cpp | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index aacc04f24..e1d058f10 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -1242,17 +1242,17 @@ namespace Duality { void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - edge_solver &SolverForEdge(Edge *edge, bool models); + edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); public: struct scoped_solver_for_edge { solver *orig_slvr; RPFP_caching *rpfp; edge_solver *es; - scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false){ + scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ rpfp = _rpfp; orig_slvr = rpfp->ls->slvr; - es = &(rpfp->SolverForEdge(edge,models)); + es = &(rpfp->SolverForEdge(edge,models,axioms)); rpfp->ls->slvr = es->slvr.get(); rpfp->AssumptionLits.swap(es->AssumptionLits); } diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 173959567..6b9f3ba00 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2895,12 +2895,18 @@ namespace Duality { timer_stop("Generalize"); } - RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models){ + RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ edge_solver &es = edge_solvers[edge]; uptr &p = es.slvr; if(!p.get()){ scoped_no_proof no_proofs_please(ctx.m()); // no proofs p.set(new solver(ctx,true,models)); // no models + if(axioms){ + RPFP::LogicSolver *ls = edge->owner->ls; + const std::vector &axs = ls->get_axioms(); + for(unsigned i = 0; i < axs.size(); i++) + p.get()->add(axs[i]); + } } return es; } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index e3a294550..277789ce9 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1247,7 +1247,7 @@ namespace Duality { slvr.pop(1); delete checker; #else - RPFP_caching::scoped_solver_for_edge(gen_cands_rpfp,edge,true /* models */); + RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); gen_cands_rpfp->Push(); Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); if(gen_cands_rpfp->Check(root) != unsat){ @@ -2004,7 +2004,7 @@ namespace Duality { } else { was_sat = true; - tree->Push(); + tree->Push(); std::vector &expansions = stack.back().expansions; #ifndef NO_DECISIONS for(unsigned i = 0; i < expansions.size(); i++){ From e077fc5cb41de38b6ef56b071d71fc4fc1a0a442 Mon Sep 17 00:00:00 2001 From: Leonardo de Moura Date: Thu, 20 Feb 2014 14:09:55 -0800 Subject: [PATCH 09/54] fix(api/python): make sure Z3 compiles using Python3 Signed-off-by: Leonardo de Moura --- scripts/mk_util.py | 16 ++++++++-------- src/api/python/z3.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 2fff61778..906c6b2c8 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -640,7 +640,7 @@ def is_CXX_gpp(): def is_clang_in_gpp_form(cc): version_string = subprocess.check_output([cc, '--version']) - return version_string.find('clang') != -1 + return str(version_string).find('clang') != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): @@ -1437,7 +1437,7 @@ def mk_config(): 'SO_EXT=.dll\n' 'SLINK=cl\n' 'SLINK_OUT_FLAG=/Fe\n' - 'OS_DEFINES=/D _WINDOWS\n') + 'OS_DEFINES=/D _WINDOWS\n') extra_opt = '' if GIT_HASH: extra_opt = '%s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) @@ -1485,7 +1485,7 @@ def mk_config(): print('Java Compiler: %s' % JAVAC) else: global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG - OS_DEFINES = "" + OS_DEFINES = "" ARITH = "internal" check_ar() CXX = find_cxx_compiler() @@ -1508,7 +1508,7 @@ def mk_config(): SLIBEXTRAFLAGS = '%s %s' % (SLIBEXTRAFLAGS,FOCI2LIB) CPPFLAGS = '%s -D_FOCI2' % CPPFLAGS else: - print "FAILED\n" + print("FAILED\n") FOCI2 = False if GIT_HASH: CPPFLAGS = '%s -DZ3GITHASH=%s' % (CPPFLAGS, GIT_HASH) @@ -1536,21 +1536,21 @@ def mk_config(): SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS - OS_DEFINES = '-D_LINUX' + OS_DEFINES = '-D_LINUX' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname == 'FreeBSD': CXXFLAGS = '%s -fno-strict-aliasing -D_FREEBSD_' % CXXFLAGS - OS_DEFINES = '-D_FREEBSD_' + OS_DEFINES = '-D_FREEBSD_' SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname[:6] == 'CYGWIN': CXXFLAGS = '%s -D_CYGWIN -fno-strict-aliasing' % CXXFLAGS - OS_DEFINES = '-D_CYGWIN' + OS_DEFINES = '-D_CYGWIN' SO_EXT = '.dll' SLIBFLAGS = '-shared' else: @@ -1586,7 +1586,7 @@ def mk_config(): config.write('SLINK_FLAGS=%s\n' % SLIBFLAGS) config.write('SLINK_EXTRA_FLAGS=%s\n' % SLIBEXTRAFLAGS) config.write('SLINK_OUT_FLAG=-o \n') - config.write('OS_DEFINES=%s\n' % OS_DEFINES) + config.write('OS_DEFINES=%s\n' % OS_DEFINES) if is_verbose(): print('Host platform: %s' % sysname) print('C++ Compiler: %s' % CXX) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 959566619..9233a41dc 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -586,7 +586,7 @@ class FuncDeclRef(AstRef): return Z3_func_decl_to_ast(self.ctx_ref(), self.ast) def as_func_decl(self): - return self.ast + return self.ast def name(self): """Return the name of the function declaration `self`. From d51d9b18f944acc436c0acead229eae0ae8e9625 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 21 Feb 2014 13:00:02 +0000 Subject: [PATCH 10/54] Bugfixes for compilation in Cygwin (WIN32 -> _WINDOWS) Signed-off-by: Christoph M. Wintersteiger --- src/duality/duality.h | 4 ++-- src/duality/duality_wrapper.h | 4 ++-- src/interp/iz3hash.h | 12 ++++++------ src/interp/iz3mgr.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index e1d058f10..7db9a4479 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -25,7 +25,7 @@ Revision History: #include // make hash_map and hash_set available -#ifndef WIN32 +#ifndef _WINDOWS using namespace stl_ext; #endif @@ -782,7 +782,7 @@ protected: }; -#ifdef WIN32 +#ifdef _WINDOWS __declspec(dllexport) #endif void FromClauses(const std::vector &clauses); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2b0045023..8a77a69da 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1402,7 +1402,7 @@ namespace hash_space { } // to make Duality::ast hashable in windows -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value(const Duality::ast& s) { @@ -1446,7 +1446,7 @@ namespace hash_space { } // to make Duality::func_decl hashable in windows -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value(const Duality::func_decl& s) { diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index 94f282265..a564026bd 100755 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -42,7 +42,7 @@ Revision History: #include #include #else -#ifdef WIN32 +#ifdef _WINDOWS #define stl_ext stdext #define hash_space std #include @@ -61,7 +61,7 @@ Revision History: // stupid STL doesn't include hash function for class string -#ifndef WIN32 +#ifndef _WINDOWS namespace stl_ext { template <> @@ -86,7 +86,7 @@ namespace hash_space { }; } -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value >(const std::pair& p) { // hash _Keyval to size_t value one-to-one @@ -112,7 +112,7 @@ size_t stdext::hash_value >(const std::pair& p) } #endif -#ifdef WIN32 +#ifdef _WINDOWS namespace std { template <> @@ -139,7 +139,7 @@ namespace std { #endif -#ifndef WIN32 +#ifndef _WINDOWS #if 0 namespace stl_ext { @@ -155,7 +155,7 @@ namespace stl_ext { #endif -#ifdef WIN32 +#ifdef _WINDOWS diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e974a199b..d9593ced5 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -127,7 +127,7 @@ namespace hash_space { } // to make ast_r hashable in windows -#ifdef WIN32 +#ifdef _WINDOWS template <> inline size_t stdext::hash_value(const ast_r& s) { From 07d56bdc705cc5caf7a6fa5776095f9d7289918a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 21 Feb 2014 13:44:39 +0000 Subject: [PATCH 11/54] Java API bugfixes for cygwin compilation Signed-off-by: Christoph M. Wintersteiger --- scripts/mk_util.py | 6 +++--- scripts/update_api.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 906c6b2c8..d425f6576 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1198,9 +1198,9 @@ class JavaDLLComponent(Component): deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile) out.write(deps) out.write('\n') - if IS_WINDOWS: - JAVAC = '"%s"' % JAVAC - JAR = '"%s"' % JAR + #if IS_WINDOWS: + JAVAC = '"%s"' % JAVAC + JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, diff --git a/scripts/update_api.py b/scripts/update_api.py index fa6111482..f88e0393f 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -523,7 +523,7 @@ def mk_java(): java_native.write(' public static class LongPtr { public long value; }\n') java_native.write(' public static class StringPtr { public String value; }\n') java_native.write(' public static native void setInternalErrorHandler(long ctx);\n\n') - if IS_WINDOWS: + if IS_WINDOWS or os.uname()[0]=="CYGWIN": java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name) else: java_native.write(' static { System.loadLibrary("%s"); }\n' % get_component('java').dll_name[3:]) # We need 3: to extract the prexi 'lib' form the dll_name @@ -588,6 +588,9 @@ def mk_java(): java_wrapper = open(java_wrapperf, 'w') pkg_str = get_component('java').package_name.replace('.', '_') java_wrapper.write('// Automatically generated file\n') + java_wrapper.write('#ifdef _CYGWIN\n') + java_wrapper.write('typedef long long __int64;\n') + java_wrapper.write('#endif\n') java_wrapper.write('#include\n') java_wrapper.write('#include\n') java_wrapper.write('#include"z3.h"\n') From 4a9f12dd3431e9df0ae9b8578df73ccfca943c69 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Feb 2014 13:57:15 +0000 Subject: [PATCH 12/54] bugfix for FPA --- src/tactic/fpa/qffpa_tactic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 90cfc8c92..73faa5b50 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -52,6 +52,8 @@ struct is_non_qffpa_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) throw found(); + if (is_uninterp(n)) + throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) return; @@ -78,6 +80,8 @@ struct is_non_qffpabv_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) throw found(); + if (is_uninterp(n)) + throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) return; From efd0cdc740e15d6095ba57aa9b22b97cea488981 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 24 Feb 2014 14:01:51 +0000 Subject: [PATCH 13/54] bugfix for FPA --- src/tactic/fpa/qffpa_tactic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 73faa5b50..772fd3996 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -52,7 +52,7 @@ struct is_non_qffpa_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) throw found(); - if (is_uninterp(n)) + if (is_uninterp(n) && n->get_num_args() != 0) throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) @@ -80,7 +80,7 @@ struct is_non_qffpabv_predicate { sort * s = get_sort(n); if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) throw found(); - if (is_uninterp(n)) + if (is_uninterp(n) && n->get_num_args() != 0) throw found(); family_id fid = s->get_family_id(); if (fid == m.get_basic_family_id()) From 7fc011dbb81242d03fb8009bc25b5dbd24b3379d Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 24 Feb 2014 11:59:02 -0800 Subject: [PATCH 14/54] interpolation fixes --- src/interp/iz3interp.cpp | 2 +- src/interp/iz3proof_itp.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index c204cc35d..73eaab841 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -64,7 +64,7 @@ struct frame_reducer : public iz3mgr { : iz3mgr(other) {} void get_proof_assumptions_rec(z3pf proof, hash_set &memo, std::vector &used_frames){ - if(memo.count(proof))return; + if(memo.find(proof) != memo.end())return; memo.insert(proof); pfrule dk = pr(proof); if(dk == PR_ASSERTED){ diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 303d90b1b..9d241398c 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -680,7 +680,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast dummy1, dummy2; sum_cond_ineq(in1,coeff2,in2,dummy1,dummy2); n1 = merge_normal_chains(n1,n2, Aproves, Bproves); - ineq = make_normal(in1,n1); + ineq = is_true(n1) ? in1 : make_normal(in1,n1); } bool is_ineq(const ast &ineq){ @@ -760,6 +760,8 @@ class iz3proof_itp_impl : public iz3proof_itp { } ast round_ineq(const ast &ineq){ + if(sym(ineq) == normal) + return make_normal(round_ineq(arg(ineq,0)),arg(ineq,1)); if(!is_ineq(ineq)) throw cannot_simplify(); ast res = simplify_ineq(ineq); @@ -1689,7 +1691,7 @@ class iz3proof_itp_impl : public iz3proof_itp { ast diff; if(comp_op == Leq) diff = make(Sub,rhs,mid); else diff = make(Sub,mid,rhs); - ast foo = z3_simplify(make(Leq,make_int("0"),diff)); + ast foo = make(Leq,make_int("0"),z3_simplify(diff)); if(is_true(cond)) cond = foo; else { From b968eb2b8cf75385523955e51f99e689b31a19b8 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 25 Feb 2014 18:13:16 +0000 Subject: [PATCH 15/54] FPA probe bugfixes Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/qffpa_tactic.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 772fd3996..04ffc281a 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -42,7 +42,7 @@ struct is_non_qffpa_predicate { ast_manager & m; float_util u; - is_non_qffpa_predicate(ast_manager & _m) :m(_m), u(m) {} + is_non_qffpa_predicate(ast_manager & _m) : m(_m), u(m) {} void operator()(var *) { throw found(); } @@ -50,11 +50,9 @@ struct is_non_qffpa_predicate { void operator()(app * n) { sort * s = get_sort(n); - if (!m.is_bool(s) && !(u.is_float(s) || u.is_rm(s))) + if (!m.is_bool(s) && !u.is_float(s) && !u.is_rm(s)) throw found(); - if (is_uninterp(n) && n->get_num_args() != 0) - throw found(); - family_id fid = s->get_family_id(); + family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == u.get_family_id()) @@ -70,7 +68,7 @@ struct is_non_qffpabv_predicate { bv_util bu; float_util fu; - is_non_qffpabv_predicate(ast_manager & _m) :m(_m), bu(m), fu(m) {} + is_non_qffpabv_predicate(ast_manager & _m) : m(_m), bu(m), fu(m) {} void operator()(var *) { throw found(); } @@ -78,11 +76,9 @@ struct is_non_qffpabv_predicate { void operator()(app * n) { sort * s = get_sort(n); - if (!m.is_bool(s) && !(fu.is_float(s) || fu.is_rm(s) || bu.is_bv_sort(s))) + if (!m.is_bool(s) && !fu.is_float(s) && !fu.is_rm(s) && !bu.is_bv_sort(s)) throw found(); - if (is_uninterp(n) && n->get_num_args() != 0) - throw found(); - family_id fid = s->get_family_id(); + family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; if (fid == fu.get_family_id() || fid == bu.get_family_id()) From 4c8bbad8d67bca51930788392792352df209564b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 25 Feb 2014 18:16:28 +0000 Subject: [PATCH 16/54] FPA probe bugfix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/fpa/qffpa_tactic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tactic/fpa/qffpa_tactic.cpp b/src/tactic/fpa/qffpa_tactic.cpp index 04ffc281a..f9b7f88a1 100644 --- a/src/tactic/fpa/qffpa_tactic.cpp +++ b/src/tactic/fpa/qffpa_tactic.cpp @@ -57,6 +57,8 @@ struct is_non_qffpa_predicate { return; if (fid == u.get_family_id()) return; + if (is_uninterp_const(n)) + return; throw found(); } @@ -83,6 +85,8 @@ struct is_non_qffpabv_predicate { return; if (fid == fu.get_family_id() || fid == bu.get_family_id()) return; + if (is_uninterp_const(n)) + return; throw found(); } From ee9907a700903cbbc86cd1e2669b019f529cb908 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 25 Feb 2014 18:19:50 -0800 Subject: [PATCH 17/54] two interpolation fixes --- src/interp/iz3proof_itp.cpp | 112 +++++++++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 21 deletions(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 9d241398c..1eb4fcc84 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -729,29 +729,31 @@ class iz3proof_itp_impl : public iz3proof_itp { ast x = arg(equality,0); ast y = arg(equality,1); ast Aproves1 = mk_true(), Bproves1 = mk_true(); - ast xleqy = round_ineq(ineq_from_chain(arg(pf,1),Aproves1,Bproves1)); - ast yleqx = round_ineq(ineq_from_chain(arg(pf,2),Aproves1,Bproves1)); + ast pf1 = destruct_cond_ineq(arg(pf,1), Aproves1, Bproves1); + ast pf2 = destruct_cond_ineq(arg(pf,2), Aproves1, Bproves1); + ast xleqy = round_ineq(ineq_from_chain(pf1,Aproves1,Bproves1)); + ast yleqx = round_ineq(ineq_from_chain(pf2,Aproves1,Bproves1)); ast ineq1 = make(Leq,make_int("0"),make_int("0")); sum_cond_ineq(ineq1,make_int("-1"),xleqy,Aproves1,Bproves1); sum_cond_ineq(ineq1,make_int("-1"),yleqx,Aproves1,Bproves1); - Bproves1 = my_and(Bproves1,z3_simplify(ineq1)); + ast Acond = my_implies(Aproves1,my_and(Bproves1,z3_simplify(ineq1))); ast Aproves2 = mk_true(), Bproves2 = mk_true(); ast ineq2 = make(Leq,make_int("0"),make_int("0")); sum_cond_ineq(ineq2,make_int("1"),xleqy,Aproves2,Bproves2); sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); - Bproves2 = z3_simplify(ineq2); - if(!is_true(Aproves1) || !is_true(Aproves2)) - throw "help!"; + ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); + // if(!is_true(Aproves1) || !is_true(Bproves1)) + // std::cout << "foo!\n";; if(get_term_type(x) == LitA){ ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,top_pos,Bproves1,make(Equal,x,iter)); - ast rewrite2 = make_rewrite(LitB,top_pos,Bproves2,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,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(y) == LitA){ ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); - ast rewrite2 = make_rewrite(LitA,top_pos,Bproves1,make(Equal,iter,y)); - ast rewrite1 = make_rewrite(LitB,top_pos,Bproves2,make(Equal,x,iter)); + ast rewrite2 = make_rewrite(LitA,top_pos,Acond,make(Equal,iter,y)); + ast rewrite1 = make_rewrite(LitB,top_pos,Bcond,make(Equal,x,iter)); return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); } throw cannot_simplify(); @@ -922,6 +924,8 @@ class iz3proof_itp_impl : public iz3proof_itp { return chain; } + struct subterm_normals_failed {}; + void get_subterm_normals(const ast &ineq1, const ast &ineq2, const ast &chain, ast &normals, const ast &pos, hash_set &memo, ast &Aproves, ast &Bproves){ opr o1 = op(ineq1); @@ -935,14 +939,77 @@ class iz3proof_itp_impl : public iz3proof_itp { get_subterm_normals(arg(ineq1,i), arg(ineq2,i), chain, normals, new_pos, memo, Aproves, Bproves); } } - else if(get_term_type(ineq2) == LitMixed && memo.find(ineq2) == memo.end()){ - memo.insert(ineq2); - ast sub_chain = extract_rewrites(chain,pos); - if(is_true(sub_chain)) - throw "bad inequality rewriting"; - ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); - normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + else if(get_term_type(ineq2) == LitMixed){ + if(memo.find(ineq2) == memo.end()){ + memo.insert(ineq2); + ast sub_chain = extract_rewrites(chain,pos); + if(is_true(sub_chain)) + throw "bad inequality rewriting"; + ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); + normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + } } + else if(!(ineq1 == ineq2)) + throw subterm_normals_failed(); + } + + ast rewrites_to_normals(const ast &ineq1, const ast &chain, ast &normals, ast &Aproves, ast &Bproves, ast &Aineqs){ + if(is_true(chain)) + return ineq1; + ast last = chain_last(chain); + ast rest = chain_rest(chain); + ast new_ineq1 = rewrites_to_normals(ineq1, rest, normals, Aproves, Bproves, Aineqs); + ast p1 = rewrite_pos(last); + ast term1; + ast coeff = arith_rewrite_coeff(new_ineq1,p1,term1); + ast res = subst_in_pos(new_ineq1,rewrite_pos(last),rewrite_rhs(last)); + ast rpos; + pos_diff(p1,rewrite_pos(last),rpos); + ast term2 = subst_in_pos(term1,rpos,rewrite_rhs(last)); + if(get_term_type(term1) != LitMixed && get_term_type(term2) != LitMixed){ + if(is_rewrite_side(LitA,last)) + linear_comb(Aineqs,coeff,make(Leq,make_int(rational(0)),make(Sub,term2,term1))); + } + else { + ast pf = extract_rewrites(make(concat,mk_true(),rest),p1); + ast new_normal = fix_normal(term1,term2,pf); + normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); + } + return res; + } + + ast arith_rewrite_coeff(const ast &ineq, ast &p1, ast &term){ + ast coeff = make_int(rational(1)); + if(p1 == top_pos){ + term = ineq; + return coeff; + } + int argpos = pos_arg(p1); + opr o = op(ineq); + switch(o){ + case Leq: + case Lt: + coeff = argpos ? make_int(rational(1)) : make_int(rational(-1)); + break; + case Geq: + case Gt: + coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); + break; + case Not: + case Plus: + break; + case Times: + coeff = arg(ineq,0); + break; + default: + p1 = top_pos; + term = ineq; + return coeff; + } + p1 = arg(p1,1); + ast res = arith_rewrite_coeff(arg(ineq,argpos),p1,term); + p1 = pos_add(argpos,p1); + return coeff == make_int(rational(1)) ? res : make(Times,coeff,res); } ast rewrite_chain_to_normal_ineq(const ast &chain, ast &Aproves, ast &Bproves){ @@ -952,17 +1019,20 @@ class iz3proof_itp_impl : public iz3proof_itp { ast ineq2 = apply_rewrite_chain(ineq1,tail); ast nc = mk_true(); hash_set memo; - get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); - ast itp; + ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); + ast Aproves_save = Aproves, Bproves_save = Bproves; try { + get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); + } + catch (const subterm_normals_failed &){ Aproves = Aproves_save; Bproves = Bproves_save; nc = mk_true(); + rewrites_to_normals(ineq1, tail, nc, Aproves, Bproves, itp); + } if(is_rewrite_side(LitA,head)){ - itp = make(Leq,make_int("0"),make_int("0")); linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form //itp = ineq1; ast mc = z3_simplify(chain_side_proves(LitB,pref)); Bproves = my_and(Bproves,mc); } else { - itp = make(Leq,make_int(rational(0)),make_int(rational(0))); ast mc = z3_simplify(chain_side_proves(LitA,pref)); Aproves = my_and(Aproves,mc); } From 4f06b347b340c1c6308795e434283b25952fd23c Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 25 Feb 2014 18:21:06 -0800 Subject: [PATCH 18/54] new hastable implementation for interp/duality --- src/interp/iz3hash.h | 542 +++++++++++++++++++++++++++++++++---------- 1 file changed, 420 insertions(+), 122 deletions(-) mode change 100755 => 100644 src/interp/iz3hash.h diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h old mode 100755 new mode 100644 index 94f282265..c628ef32b --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -7,7 +7,16 @@ Module Name: Abstract: - Wrapper for stl hash tables + Simple implementation of bucket-list hash tables conforming to SGI + hash_map and hash_set interfaces. Just enough members are + implemented to support iz3 and duality. + + iz3 and duality need this package because they assume that insert + preserves iterators and references to elements, which is not true + of the hashtable packages in util. + + This package lives in namespace hash_space. Specializations of + class "hash" should be made in this namespace. Author: @@ -17,66 +26,36 @@ Revision History: --*/ -// pull in the headers for has_map and hash_set -// these live in non-standard places - #ifndef IZ3_HASH_H #define IZ3_HASH_H -//#define USE_UNORDERED_MAP -#ifdef USE_UNORDERED_MAP - -#define stl_ext std -#define hash_space std -#include -#include -#define hash_map unordered_map -#define hash_set unordered_set - -#else - -#if __GNUC__ >= 3 -#undef __DEPRECATED -#define stl_ext __gnu_cxx -#define hash_space stl_ext -#include -#include -#else -#ifdef WIN32 -#define stl_ext stdext -#define hash_space std -#include -#include -#else -#define stl_ext std -#define hash_space std -#include -#include -#endif -#endif - -#endif - #include +#include +#include +#include "hash.h" -// stupid STL doesn't include hash function for class string - -#ifndef WIN32 - -namespace stl_ext { - template <> - class hash { - stl_ext::hash H; - public: - size_t operator()(const std::string &s) const { - return H(s.c_str()); - } - }; -} - -#endif +#define stl_ext hash_space namespace hash_space { + + template class hash {}; + + template <> + class hash { + public: + size_t operator()(const int &s) const { + return s; + } + }; + + template <> + class hash { + public: + size_t operator()(const std::string &s) const { + return string_hash(s.c_str(), s.size(), 0); + } + }; + template <> class hash > { public: @@ -84,17 +63,7 @@ namespace hash_space { return p.first + p.second; } }; -} -#ifdef WIN32 -template <> inline -size_t stdext::hash_value >(const std::pair& p) -{ // hash _Keyval to size_t value one-to-one - return p.first + p.second; -} -#endif - -namespace hash_space { template class hash > { public: @@ -102,70 +71,399 @@ namespace hash_space { return (size_t)p.first + (size_t)p.second; } }; -} -#if 0 -template inline -size_t stdext::hash_value >(const std::pair& p) -{ // hash _Keyval to size_t value one-to-one - return (size_t)p.first + (size_t)p.second; -} -#endif + enum { num_primes = 29 }; -#ifdef WIN32 + static const unsigned long primes[num_primes] = + { + 7ul, + 53ul, + 97ul, + 193ul, + 389ul, + 769ul, + 1543ul, + 3079ul, + 6151ul, + 12289ul, + 24593ul, + 49157ul, + 98317ul, + 196613ul, + 393241ul, + 786433ul, + 1572869ul, + 3145739ul, + 6291469ul, + 12582917ul, + 25165843ul, + 50331653ul, + 100663319ul, + 201326611ul, + 402653189ul, + 805306457ul, + 1610612741ul, + 3221225473ul, + 4294967291ul + }; -namespace std { - template <> - class less > { - public: - bool operator()(const pair &x, const pair &y) const { - return x.first < y.first || x.first == y.first && x.second < y.second; - } - }; - -} - -namespace std { - template - class less > { - public: - bool operator()(const pair &x, const pair &y) const { - return (size_t)x.first < (size_t)y.first || (size_t)x.first == (size_t)y.first && (size_t)x.second < (size_t)y.second; - } - }; - -} + inline unsigned long next_prime(unsigned long n) { + const unsigned long* to = primes + (int)num_primes; + for(const unsigned long* p = primes; p < to; p++) + if(*p >= n) return *p; + return primes[num_primes-1]; + } -#endif - - -#ifndef WIN32 - -#if 0 -namespace stl_ext { - template - class hash { + template + class hashtable + { public: - size_t operator()(const T *p) const { - return (size_t) p; + + typedef Value &reference; + typedef const Value &const_reference; + + struct Entry + { + Entry* next; + Value val; + + Entry(const Value &_val) : val(_val) {next = 0;} + }; + + + struct iterator + { + Entry* ent; + hashtable* tab; + + typedef std::forward_iterator_tag iterator_category; + typedef Value value_type; + typedef std::ptrdiff_t difference_type; + typedef size_t size_type; + typedef Value& reference; + typedef Value* pointer; + + iterator(Entry* _ent, hashtable* _tab) : ent(_ent), tab(_tab) { } + + iterator() { } + + Value &operator*() const { return ent->val; } + + Value *operator->() const { return &(operator*()); } + + iterator &operator++() { + Entry *old = ent; + ent = ent->next; + if (!ent) { + size_t bucket = tab->get_bucket(old->val); + while (!ent && ++bucket < tab->buckets.size()) + ent = tab->buckets[bucket]; + } + return *this; + } + + iterator operator++(int) { + iterator tmp = *this; + operator++(); + return tmp; + } + + + bool operator==(const iterator& it) const { + return ent == it.ent; + } + + bool operator!=(const iterator& it) const { + return ent != it.ent; + } + }; + + struct const_iterator + { + const Entry* ent; + const hashtable* tab; + + typedef std::forward_iterator_tag iterator_category; + typedef Value value_type; + typedef std::ptrdiff_t difference_type; + typedef size_t size_type; + typedef const Value& reference; + typedef const Value* pointer; + + const_iterator(const Entry* _ent, const hashtable* _tab) : ent(_ent), tab(_tab) { } + + const_iterator() { } + + const Value &operator*() const { return ent->val; } + + const Value *operator->() const { return &(operator*()); } + + const_iterator &operator++() { + Entry *old = ent; + ent = ent->next; + if (!ent) { + size_t bucket = tab->get_bucket(old->val); + while (!ent && ++bucket < tab->buckets.size()) + ent = tab->buckets[bucket]; + } + return *this; + } + + const_iterator operator++(int) { + const_iterator tmp = *this; + operator++(); + return tmp; + } + + + bool operator==(const const_iterator& it) const { + return ent == it.ent; + } + + bool operator!=(const const_iterator& it) const { + return ent != it.ent; + } + }; + + private: + + typedef std::vector Table; + + Table buckets; + size_t entries; + HashFun hash_fun ; + GetKey get_key; + KeyEqFun key_eq_fun; + + public: + + hashtable(size_t init_size) : buckets(init_size,(Entry *)0) { + entries = 0; + } + + hashtable(const hashtable& other) { + dup(other); + } + + hashtable& operator= (const hashtable& other) { + if (&other != this) + dup(other); + return *this; + } + + ~hashtable() { + clear(); + } + + size_t size() const { + return entries; + } + + bool empty() const { + return size() == 0; + } + + void swap(hashtable& other) { + buckets.swap(other.buckets); + std::swap(entries, other.entries); + } + + iterator begin() { + for (size_t i = 0; i < buckets.size(); ++i) + if (buckets[i]) + return iterator(buckets[i], this); + return end(); + } + + iterator end() { + return iterator(0, this); + } + + const_iterator begin() const { + for (size_t i = 0; i < buckets.size(); ++i) + if (buckets[i]) + return const_iterator(buckets[i], this); + return end(); + } + + const_iterator end() const { + return const_iterator(0, this); + } + + size_t get_bucket(const Value& val, size_t n) const { + return hash_fun(get_key(val)) % n; + } + + size_t get_key_bucket(const Key& key) const { + return hash_fun(key) % buckets.size(); + } + + size_t get_bucket(const Value& val) const { + return get_bucket(val,buckets.size()); + } + + Entry *lookup(const Value& val, bool ins = false) + { + resize(entries + 1); + + size_t n = get_bucket(val); + Entry* from = buckets[n]; + + for (Entry* ent = from; ent; ent = ent->next) + if (key_eq_fun(get_key(ent->val), get_key(val))) + return ent; + + if(!ins) return 0; + + Entry* tmp = new Entry(val); + tmp->next = from; + buckets[n] = tmp; + ++entries; + return tmp; + } + + Entry *lookup_key(const Key& key) const + { + size_t n = get_key_bucket(key); + Entry* from = buckets[n]; + + for (Entry* ent = from; ent; ent = ent->next) + if (key_eq_fun(get_key(ent->val), key)) + return ent; + + return 0; + } + + const_iterator find(const Key& key) const { + return const_iterator(lookup_key(key),this); + } + + iterator find(const Key& key) { + return iterator(lookup_key(key),this); + } + + std::pair insert(const Value& val){ + size_t old_entries = entries; + Entry *ent = lookup(val,true); + return std::pair(iterator(ent,this),entries > old_entries); + } + + iterator insert(const iterator &it, const Value& val){ + Entry *ent = lookup(val,true); + return iterator(ent,this); + } + + size_t erase(const Key& key) + { + Entry** p = &(buckets[get_key_bucket(key)]); + size_t count = 0; + while(*p){ + Entry *q = *p; + if (key_eq_fun(get_key(q->val), key)) { + ++count; + *p = q->next; + delete q; + } + else + p = &(q->next); + } + entries -= count; + return count; + } + + void resize(size_t new_size) { + const size_t old_n = buckets.size(); + if (new_size <= old_n) return; + const size_t n = next_prime(new_size); + if (n <= old_n) return; + Table tmp(n, (Entry*)(0)); + for (size_t i = 0; i < old_n; ++i) { + Entry* ent = buckets[i]; + while (ent) { + size_t new_bucket = get_bucket(ent->val, n); + buckets[i] = ent->next; + ent->next = tmp[new_bucket]; + tmp[new_bucket] = ent; + ent = buckets[i]; + } + } + buckets.swap(tmp); + } + + void clear() + { + for (size_t i = 0; i < buckets.size(); ++i) { + for (Entry* ent = buckets[i]; ent != 0;) { + Entry* next = ent->next; + delete ent; + ent = next; + } + buckets[i] = 0; + } + entries = 0; + } + + void dup(const hashtable& other) + { + buckets.resize(other.buckets.size()); + for (size_t i = 0; i < other.buckets.size(); ++i) { + Entry** to = &buckets[i]; + for (Entry* from = other.buckets[i]; from; from = from->next) + to = &((*to = new Entry(from->val))->next); + } + entries = other.entries; } }; + + template + class equal { + public: + bool operator()(const T& x, const T &y) const { + return x == y; + } + }; + + template + class identity { + public: + const T &operator()(const T &x) const { + return x; + } + }; + + template + class proj1 { + public: + const T &operator()(const std::pair &x) const { + return x.first; + } + }; + + template , + class EqFun = equal > + class hash_set + : public hashtable,EqFun> { + + public: + hash_set() + : hashtable,EqFun>(7) {} + }; + + template , + class EqFun = equal > + class hash_map + : public hashtable,Key,HashFun,proj1,EqFun> { + + public: + + hash_map() + : hashtable,Key,HashFun,proj1,EqFun>(7) {} + + Value &operator[](const Key& key) { + std::pair kvp(key,Value()); + return lookup(kvp,true)->val.second; + } + }; + } #endif - -#endif - -#ifdef WIN32 - - - - -template -class hash_map : public stl_ext::hash_map > > {}; - -template -class hash_set : public stl_ext::hash_set > > {}; - -#endif - -#endif From 891d0d07f84ba2c7c1001bca64e6b1b3d08b382e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 27 Feb 2014 13:42:40 -0800 Subject: [PATCH 19/54] removig unsound simplification in ctx-solver-simplify tactic Signed-off-by: Nikolaj Bjorner --- src/smt/tactic/ctx_solver_simplify_tactic.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 8cefee8da..dcd5cd9e1 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -211,11 +211,7 @@ protected: // found.push_back(arg); - if (path_r.first == self_pos) { - TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << " |-> " << mk_pp(path_r.second, m) << "\n";); - args.push_back(path_r.second); - } - else if (m.is_bool(arg)) { + if (m.is_bool(arg)) { res = local_simplify(a, n, id, i); TRACE("ctx_solver_simplify_tactic", tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";); From 08d892bbdbf1f40d0ebca0d7e9c7479b75b1bd37 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Feb 2014 17:21:47 -0800 Subject: [PATCH 20/54] interpolation fixes --- src/duality/duality.h | 2 ++ src/duality/duality_rpfp.cpp | 19 ++++++++++++ src/duality/duality_solver.cpp | 9 ++++-- src/interp/iz3proof_itp.cpp | 55 ++++++++++++++++++++++------------ 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index e1d058f10..12ba21516 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -82,6 +82,8 @@ namespace Duality { Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); + Term CloneQuantAndSimp(const expr &t, const expr &body); + Term RemoveRedundancy(const Term &t); Term IneqToEq(const Term &t); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 5751fa890..e09278dd4 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -523,6 +523,25 @@ namespace Duality { return foo; } + Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ + if(t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == And){ + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i)); + return ctx.make(And,args); + } + if(!t.is_quantifier_forall() && body.is_app() && body.decl().get_decl_kind() == Or){ + int nargs = body.num_args(); + std::vector args(nargs); + for(int i = 0; i < nargs; i++) + args[i] = CloneQuantAndSimp(t, body.arg(i)); + return ctx.make(Or,args); + } + return clone_quantifier(t,body); + } + + Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val){ std::pair foo(t,expr(ctx)); std::pair::iterator, bool> bar = memo.insert(foo); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 1c2f2c927..c7eec7e9f 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1916,6 +1916,7 @@ namespace Duality { stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; + int update_failures = 0; while (true) { @@ -1954,10 +1955,14 @@ namespace Duality { heuristic->Update(node->map); // make it less likely to expand this node in future } if(update_count == 0){ - if(was_sat) - throw Incompleteness(); + if(was_sat){ + update_failures++; + if(update_failures > 10) + throw Incompleteness(); + } reporter->Message("backtracked without learning"); } + else update_failures = 0; } tree->ComputeProofCore(); // need to compute the proof core before popping solver bool propagated = false; diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 1eb4fcc84..a1a332005 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -369,11 +369,17 @@ class iz3proof_itp_impl : public iz3proof_itp { } default: { - symb s = sym(itp2); - if(s == sforall || s == sexists) - res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); - else + opr o = op(itp2); + if(o == Uninterpreted){ + symb s = sym(itp2); + if(s == sforall || s == sexists) + res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); + else + res = itp2; + } + else { res = itp2; + } } } } @@ -405,11 +411,17 @@ class iz3proof_itp_impl : public iz3proof_itp { } default: { - symb s = sym(itp1); - if(s == sforall || s == sexists) - res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); - else + opr o = op(itp1); + if(o == Uninterpreted){ + symb s = sym(itp1); + if(s == sforall || s == sexists) + res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); + else + res = itp1; + } + else { res = itp1; + } } } } @@ -464,18 +476,20 @@ class iz3proof_itp_impl : public iz3proof_itp { std::pair::iterator,bool> bar = subst_memo.insert(foo); ast &res = bar.first->second; if(bar.second){ - symb g = sym(e); - if(g == rotate_sum){ - if(var == get_placeholder(arg(e,0))){ - res = e; + if(op(e) == Uninterpreted){ + symb g = sym(e); + if(g == rotate_sum){ + if(var == get_placeholder(arg(e,0))){ + res = e; + } + else + res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); + return res; + } + if(g == concat){ + res = e; + return res; } - else - res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); - return res; - } - if(g == concat){ - res = e; - return res; } int nargs = num_args(e); std::vector args(nargs); @@ -794,6 +808,7 @@ class iz3proof_itp_impl : public iz3proof_itp { return simplify_sum(args); } + ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ if(pl == arg(pf,1)){ ast cond = mk_true(); @@ -1036,6 +1051,8 @@ class iz3proof_itp_impl : public iz3proof_itp { ast mc = z3_simplify(chain_side_proves(LitA,pref)); Aproves = my_and(Aproves,mc); } + if(is_true(nc)) + return itp; return make_normal(itp,nc); } From acf4ad0ab670d1714e5a82aee7b4ac2c84ecd74a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Thu, 27 Feb 2014 17:23:19 -0800 Subject: [PATCH 21/54] use new hashtable implementation in windows --- src/api/api_interp.cpp | 4 -- src/duality/duality.h | 2 - src/duality/duality_solver.cpp | 2 +- src/duality/duality_wrapper.h | 16 -------- src/interp/iz3base.cpp | 2 - src/interp/iz3checker.cpp | 2 - src/interp/iz3foci.cpp | 2 - src/interp/iz3hash.h | 3 ++ src/interp/iz3interp.cpp | 2 - src/interp/iz3mgr.cpp | 2 - src/interp/iz3mgr.h | 8 ---- src/interp/iz3pp.cpp | 4 -- src/interp/iz3proof_itp.cpp | 2 - src/interp/iz3translate.cpp | 2 - src/interp/iz3translate_direct.cpp | 65 ------------------------------ 15 files changed, 4 insertions(+), 114 deletions(-) diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 98722022c..2c88a1978 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -39,11 +39,8 @@ Revision History: #include"iz3pp.h" #include"iz3checker.h" -#ifndef WIN32 using namespace stl_ext; -#endif -#ifndef WIN32 // WARNING: don't make a hash_map with this if the range type // has a destructor: you'll get an address dependency!!! namespace stl_ext { @@ -55,7 +52,6 @@ namespace stl_ext { } }; } -#endif typedef interpolation_options_struct *Z3_interpolation_options; diff --git a/src/duality/duality.h b/src/duality/duality.h index e1d058f10..479c90a29 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -25,9 +25,7 @@ Revision History: #include // make hash_map and hash_set available -#ifndef WIN32 using namespace stl_ext; -#endif namespace Duality { diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 1c2f2c927..6079e17d9 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1955,7 +1955,7 @@ namespace Duality { } if(update_count == 0){ if(was_sat) - throw Incompleteness(); + throw "Help!"; reporter->Message("backtracked without learning"); } } diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2b0045023..90f1799a6 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -1401,14 +1401,6 @@ namespace hash_space { }; } -// to make Duality::ast hashable in windows -#ifdef WIN32 -template <> inline -size_t stdext::hash_value(const Duality::ast& s) -{ - return s.raw()->get_id(); -} -#endif // to make Duality::ast usable in ordered collections namespace std { @@ -1445,14 +1437,6 @@ namespace hash_space { }; } -// to make Duality::func_decl hashable in windows -#ifdef WIN32 -template <> inline -size_t stdext::hash_value(const Duality::func_decl& s) -{ - return s.raw()->get_id(); -} -#endif // to make Duality::func_decl usable in ordered collections namespace std { diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 26146bfa3..054c8a811 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -34,9 +34,7 @@ Revision History: #include "../smt/smt_solver.h" -#ifndef WIN32 using namespace stl_ext; -#endif iz3base::range &iz3base::ast_range(ast t){ diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp index d423ba48f..02acaa5c1 100755 --- a/src/interp/iz3checker.cpp +++ b/src/interp/iz3checker.cpp @@ -36,9 +36,7 @@ Revision History: #include -#ifndef WIN32 using namespace stl_ext; -#endif struct iz3checker : iz3base { diff --git a/src/interp/iz3foci.cpp b/src/interp/iz3foci.cpp index 1d81a1b15..527e73ae4 100755 --- a/src/interp/iz3foci.cpp +++ b/src/interp/iz3foci.cpp @@ -25,9 +25,7 @@ Revision History: #include "foci2.h" #include "iz3foci.h" -#ifndef WIN32 using namespace stl_ext; -#endif class iz3foci_impl : public iz3secondary { diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h index c628ef32b..355c03817 100644 --- a/src/interp/iz3hash.h +++ b/src/interp/iz3hash.h @@ -445,6 +445,9 @@ namespace hash_space { : public hashtable,EqFun> { public: + + typedef Element value_type; + hash_set() : hashtable,EqFun>(7) {} }; diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp index 73eaab841..667d962d0 100755 --- a/src/interp/iz3interp.cpp +++ b/src/interp/iz3interp.cpp @@ -43,9 +43,7 @@ Revision History: -#ifndef WIN32 using namespace stl_ext; -#endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp index 35dc67bcd..0259f1b52 100755 --- a/src/interp/iz3mgr.cpp +++ b/src/interp/iz3mgr.cpp @@ -38,9 +38,7 @@ Revision History: #include "params.h" -#ifndef WIN32 using namespace stl_ext; -#endif std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e974a199b..4ef594cee 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -126,14 +126,6 @@ namespace hash_space { }; } -// to make ast_r hashable in windows -#ifdef WIN32 -template <> inline -size_t stdext::hash_value(const ast_r& s) -{ - return s.raw()->get_id(); -} -#endif // to make ast_r usable in ordered collections namespace std { diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp index 31b375c0f..d530a3bc6 100644 --- a/src/interp/iz3pp.cpp +++ b/src/interp/iz3pp.cpp @@ -36,11 +36,8 @@ Revision History: #include"expr_abstract.h" -#ifndef WIN32 using namespace stl_ext; -#endif -#ifndef WIN32 // We promise not to use this for hash_map with range destructor namespace stl_ext { template <> @@ -51,7 +48,6 @@ namespace stl_ext { } }; } -#endif // TBD: algebraic data-types declarations will not be printed. diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 1eb4fcc84..6a150ad38 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -26,9 +26,7 @@ Revision History: #include "iz3proof_itp.h" -#ifndef WIN32 using namespace stl_ext; -#endif // #define INVARIANT_CHECKING diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 9eac5f040..7e0fa65d6 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -39,9 +39,7 @@ Revision History: #include //using std::vector; -#ifndef WIN32 using namespace stl_ext; -#endif diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp index a85c9a1c5..a42c7034d 100755 --- a/src/interp/iz3translate_direct.cpp +++ b/src/interp/iz3translate_direct.cpp @@ -42,11 +42,7 @@ Revision History: #include //using std::vector; -#ifndef WIN32 using namespace stl_ext; -#endif - -#ifndef WIN32 /* This can introduce an address dependency if the range type of hash_map has a destructor. Since the code in this file is not used and only here for @@ -62,9 +58,6 @@ namespace stl_ext { }; } -#endif - - static int lemma_count = 0; #if 0 static int nll_lemma_count = 0; @@ -96,38 +89,12 @@ namespace hash_space { }; } -#ifdef WIN32 - -template <> inline -size_t stdext::hash_value(const Z3_resolvent& p) -{ - std::hash h; - return h(p); -} - - -namespace std { - template <> - class less { - public: - bool operator()(const Z3_resolvent &x, const Z3_resolvent &y) const { - size_t ixproof = (size_t) x.proof.raw(); - size_t iyproof = (size_t) y.proof.raw(); - if(ixproof < iyproof) return true; - if(ixproof > iyproof) return false; - return x.pivot < y.pivot; - } - }; -} - -#else bool operator==(const Z3_resolvent &x, const Z3_resolvent &y) { return x.proof == y.proof && x.pivot == y.pivot; } -#endif typedef std::vector ResolventAppSet; @@ -151,36 +118,6 @@ namespace hash_space { }; } -#ifdef WIN32 - -template <> inline -size_t stdext::hash_value(const non_local_lits& p) -{ - std::hash h; - return h(p); -} - -namespace std { - template <> - class less { - public: - bool operator()(const non_local_lits &x, const non_local_lits &y) const { - ResolventAppSet::const_iterator itx = x.proofs.begin(); - ResolventAppSet::const_iterator ity = y.proofs.begin(); - while(true){ - if(ity == y.proofs.end()) return false; - if(itx == x.proofs.end()) return true; - size_t xi = (size_t) *itx; - size_t yi = (size_t) *ity; - if(xi < yi) return true; - if(xi > yi) return false; - ++itx; ++ity; - } - } - }; -} - -#else bool operator==(const non_local_lits &x, const non_local_lits &y) { ResolventAppSet::const_iterator itx = x.proofs.begin(); @@ -194,8 +131,6 @@ bool operator==(const non_local_lits &x, const non_local_lits &y) { } -#endif - /* This translator goes directly from Z3 proofs to interpolatable proofs without an intermediate representation as an iz3proof. */ From 19dbd02e1344c22552da196e52e9d0b0ba413d0a Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 28 Feb 2014 10:38:35 -0800 Subject: [PATCH 22/54] interpolation fix --- src/interp/iz3proof_itp.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index a1a332005..e66bda5f3 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -2635,6 +2635,11 @@ class iz3proof_itp_impl : public iz3proof_itp { } hash_map::iterator it = localization_map.find(e); + + if(it != localization_map.end() && is_bool_type(get_type(e)) + && !pv->ranges_intersect(pv->ast_scope(it->second),rng)) + it = localization_map.end(); // prevent quantifiers over booleans + if(it != localization_map.end()){ pf = localization_pf_map[e]; e = it->second; From f2ecd70e654c072b86860d9642935c0beb0c525b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 1 Mar 2014 11:02:18 -0800 Subject: [PATCH 23/54] fix ctx solver simplify, remove horn inequalities Signed-off-by: Nikolaj Bjorner --- src/smt/params/theory_arith_params.h | 3 +- src/smt/smt_setup.cpp | 7 - src/smt/tactic/ctx_solver_simplify_tactic.cpp | 107 +- src/smt/theory_horn_ineq.cpp | 236 ---- src/smt/theory_horn_ineq.h | 328 ----- src/smt/theory_horn_ineq_def.h | 1166 ----------------- 6 files changed, 36 insertions(+), 1811 deletions(-) delete mode 100644 src/smt/theory_horn_ineq.cpp delete mode 100644 src/smt/theory_horn_ineq.h delete mode 100644 src/smt/theory_horn_ineq_def.h diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 30bc65b6d..911c75d36 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -27,8 +27,7 @@ enum arith_solver_id { AS_DIFF_LOGIC, AS_ARITH, AS_DENSE_DIFF_LOGIC, - AS_UTVPI, - AS_HORN + AS_UTVPI }; enum bound_prop_mode { diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index f91a58f87..51b83d61c 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -22,7 +22,6 @@ Revision History: #include"theory_arith.h" #include"theory_dense_diff_logic.h" #include"theory_diff_logic.h" -#include"theory_horn_ineq.h" #include"theory_utvpi.h" #include"theory_array.h" #include"theory_array_full.h" @@ -725,12 +724,6 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_dense_mi, m_manager, m_params)); } break; - case AS_HORN: - if (m_params.m_arith_int_only) - m_context.register_plugin(alloc(smt::theory_ihi, m_manager)); - else - m_context.register_plugin(alloc(smt::theory_rhi, m_manager)); - break; case AS_UTVPI: if (m_params.m_arith_int_only) m_context.register_plugin(alloc(smt::theory_iutvpi, m_manager)); diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index dcd5cd9e1..98b7592c8 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -139,22 +139,32 @@ protected: SASSERT(g.is_well_sorted()); } + struct expr_pos { + unsigned m_parent; + unsigned m_self; + unsigned m_idx; + expr* m_expr; + expr_pos(unsigned p, unsigned s, unsigned i, expr* e): + m_parent(p), m_self(s), m_idx(i), m_expr(e) + {} + expr_pos(): + m_parent(0), m_self(0), m_idx(0), m_expr(0) + {} + }; + void reduce(expr_ref& result){ SASSERT(m.is_bool(result)); - ptr_vector todo; ptr_vector names; - svector is_checked; - svector parent_ids, self_ids; + svector todo; expr_ref_vector fresh_vars(m), trail(m); expr_ref res(m), tmp(m); - obj_map > cache; - unsigned id = 1; + obj_map cache; + unsigned id = 1, child_id = 0; expr_ref n2(m), fml(m); - unsigned path_id = 0, self_pos = 0; + unsigned parent_pos = 0, self_pos = 0, self_idx = 0; app * a; unsigned sz; - std::pair path_r; - ptr_vector found; + expr_pos path_r; expr_ref_vector args(m); expr_ref n = mk_fresh(id, m.mk_bool_sort()); trail.push_back(n); @@ -163,26 +173,25 @@ protected: tmp = m.mk_not(m.mk_iff(fml, n)); m_solver.assert_expr(tmp); - todo.push_back(fml); + todo.push_back(expr_pos(0,0,0,fml)); names.push_back(n); - is_checked.push_back(false); - parent_ids.push_back(0); - self_ids.push_back(0); m_solver.push(); while (!todo.empty() && !m_cancel) { expr_ref res(m); args.reset(); - expr* e = todo.back(); - unsigned pos = parent_ids.back(); + expr* e = todo.back().m_expr; + self_pos = todo.back().m_self; + parent_pos = todo.back().m_parent; + self_idx = todo.back().m_idx; n = names.back(); - bool checked = is_checked.back(); if (cache.contains(e)) { goto done; } - if (m.is_bool(e) && !checked && simplify_bool(n, res)) { - TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); + if (m.is_bool(e) && simplify_bool(n, res)) { + TRACE("ctx_solver_simplify_tactic", + tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); goto done; } if (!is_app(e)) { @@ -191,45 +200,31 @@ protected: } a = to_app(e); - if (!is_checked.back()) { - self_ids.back() = ++path_id; - is_checked.back() = true; - } - self_pos = self_ids.back(); - sz = a->get_num_args(); - + sz = a->get_num_args(); n2 = 0; - found.reset(); // arguments already simplified. for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); - if (cache.find(arg, path_r) && !found.contains(arg)) { + if (cache.find(arg, path_r)) { // // This is a single traversal version of the context // simplifier. It simplifies only the first occurrence of // a sub-term with respect to the context. // - found.push_back(arg); - if (m.is_bool(arg)) { - res = local_simplify(a, n, id, i); - TRACE("ctx_solver_simplify_tactic", - tout << "Already cached: " << path_r.first << " " << mk_pp(arg, m) << " |-> " << mk_pp(res, m) << "\n";); - args.push_back(res); + if (path_r.m_parent == self_pos && path_r.m_idx == i) { + args.push_back(path_r.m_expr); } else { args.push_back(arg); } } - else if (!n2 && !found.contains(arg)) { + else if (!n2) { n2 = mk_fresh(id, m.get_sort(arg)); trail.push_back(n2); - todo.push_back(arg); - parent_ids.push_back(self_pos); - self_ids.push_back(0); + todo.push_back(expr_pos(self_pos, child_id++, i, arg)); names.push_back(n2); args.push_back(n2); - is_checked.push_back(false); } else { args.push_back(arg); @@ -247,22 +242,16 @@ protected: done: if (res) { - cache.insert(e, std::make_pair(pos, res)); - } - - TRACE("ctx_solver_simplify_tactic", - tout << mk_pp(e, m) << " checked: " << checked << " cached: " << mk_pp(res?res.get():e, m) << "\n";); + cache.insert(e, expr_pos(parent_pos, self_pos, self_idx, res)); + } todo.pop_back(); - parent_ids.pop_back(); - self_ids.pop_back(); names.pop_back(); - is_checked.pop_back(); m_solver.pop(1); } if (!m_cancel) { VERIFY(cache.find(fml, path_r)); - result = path_r.second; + result = path_r.m_expr; } } @@ -302,32 +291,6 @@ protected: } return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m); } - - - expr_ref local_simplify(app* a, expr* n, unsigned& id, unsigned index) { - SASSERT(index < a->get_num_args()); - SASSERT(m.is_bool(a->get_arg(index))); - expr_ref n2(m), result(m), tmp(m); - n2 = mk_fresh(id, m.get_sort(a->get_arg(index))); - ptr_buffer args; - for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (i == index) { - args.push_back(n2); - } - else { - args.push_back(a->get_arg(i)); - } - } - m_mk_app(a->get_decl(), args.size(), args.c_ptr(), result); - m_solver.push(); - tmp = m.mk_eq(result, n); - m_solver.assert_expr(tmp); - if (!simplify_bool(n2, result)) { - result = a->get_arg(index); - } - m_solver.pop(1); - return result; - } }; diff --git a/src/smt/theory_horn_ineq.cpp b/src/smt/theory_horn_ineq.cpp deleted file mode 100644 index 978b5b003..000000000 --- a/src/smt/theory_horn_ineq.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - theory_horn_ineq.h - -Author: - - Nikolaj Bjorner (nbjorner) 2013-04-18 - -Revision History: - - The implementaton is derived from theory_diff_logic. - ---*/ -#include "theory_horn_ineq.h" -#include "theory_horn_ineq_def.h" - -namespace smt { - - template class theory_horn_ineq; - template class theory_horn_ineq; - - // similar to test_diff_logic: - - horn_ineq_tester::horn_ineq_tester(ast_manager& m): m(m), a(m) {} - - bool horn_ineq_tester::operator()(expr* e) { - m_todo.reset(); - m_pols.reset(); - pos_mark.reset(); - neg_mark.reset(); - m_todo.push_back(e); - m_pols.push_back(l_true); - while (!m_todo.empty()) { - expr* e = m_todo.back(); - lbool p = m_pols.back(); - m_todo.pop_back(); - m_pols.pop_back(); - switch (p) { - case l_true: - if (pos_mark.is_marked(e)) { - continue; - } - pos_mark.mark(e, true); - break; - case l_false: - if (neg_mark.is_marked(e)) { - continue; - } - neg_mark.mark(e, true); - break; - case l_undef: - if (pos_mark.is_marked(e) && neg_mark.is_marked(e)) { - continue; - } - pos_mark.mark(e, true); - neg_mark.mark(e, true); - break; - } - if (!test_expr(p, e)) { - return false; - } - } - return true; - } - - vector > const& horn_ineq_tester::get_linearization() const { - return m_terms; - } - - bool horn_ineq_tester::test_expr(lbool p, expr* e) { - expr* e1, *e2, *e3; - if (is_var(e)) { - return true; - } - if (!is_app(e)) { - return false; - } - app* ap = to_app(e); - if (m.is_and(ap) || m.is_or(ap)) { - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - m_todo.push_back(ap->get_arg(i)); - m_pols.push_back(p); - } - } - else if (m.is_not(e, e1)) { - m_todo.push_back(e); - m_pols.push_back(~p); - } - else if (m.is_ite(e, e1, e2, e3)) { - m_todo.push_back(e1); - m_pols.push_back(l_undef); - m_todo.push_back(e2); - m_pols.push_back(p); - m_todo.push_back(e3); - m_pols.push_back(p); - } - else if (m.is_iff(e, e1, e2)) { - m_todo.push_back(e1); - m_pols.push_back(l_undef); - m_todo.push_back(e2); - m_pols.push_back(l_undef); - m_todo.push_back(e2); - } - else if (m.is_implies(e, e1, e2)) { - m_todo.push_back(e1); - m_pols.push_back(~p); - m_todo.push_back(e2); - m_pols.push_back(p); - } - else if (m.is_eq(e, e1, e2)) { - return linearize(e1, e2) == diff; - } - else if (m.is_true(e) || m.is_false(e)) { - // no-op - } - else if (a.is_le(e, e1, e2) || a.is_ge(e, e2, e1) || - a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) { - if (p == l_false) { - std::swap(e2, e1); - } - classify_t cl = linearize(e1, e2); - switch(p) { - case l_undef: - return cl == diff; - case l_true: - case l_false: - return cl == horn || cl == diff; - } - } - else if (!is_uninterp_const(e)) { - return false; - } - return true; - } - - bool horn_ineq_tester::operator()(unsigned num_fmls, expr* const* fmls) { - for (unsigned i = 0; i < num_fmls; ++i) { - if (!(*this)(fmls[i])) { - return false; - } - } - return true; - } - - horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e) { - m_terms.reset(); - m_terms.push_back(std::make_pair(e, rational(1))); - return linearize(); - } - - horn_ineq_tester::classify_t horn_ineq_tester::linearize(expr* e1, expr* e2) { - m_terms.reset(); - m_terms.push_back(std::make_pair(e1, rational(1))); - m_terms.push_back(std::make_pair(e2, rational(-1))); - return linearize(); - } - - horn_ineq_tester::classify_t horn_ineq_tester::linearize() { - - m_weight.reset(); - m_coeff_map.reset(); - - while (!m_terms.empty()) { - expr* e1, *e2; - rational num; - rational mul = m_terms.back().second; - expr* e = m_terms.back().first; - m_terms.pop_back(); - if (a.is_add(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - m_terms.push_back(std::make_pair(to_app(e)->get_arg(i), mul)); - } - } - else if (a.is_mul(e, e1, e2) && a.is_numeral(e1, num)) { - m_terms.push_back(std::make_pair(e2, mul*num)); - } - else if (a.is_mul(e, e2, e1) && a.is_numeral(e1, num)) { - m_terms.push_back(std::make_pair(e2, mul*num)); - } - else if (a.is_sub(e, e1, e2)) { - m_terms.push_back(std::make_pair(e1, mul)); - m_terms.push_back(std::make_pair(e2, -mul)); - } - else if (a.is_uminus(e, e1)) { - m_terms.push_back(std::make_pair(e1, -mul)); - } - else if (a.is_numeral(e, num)) { - m_weight += num*mul; - } - else if (a.is_to_real(e, e1)) { - m_terms.push_back(std::make_pair(e1, mul)); - } - else if (!is_uninterp_const(e)) { - return non_horn; - } - else { - m_coeff_map.insert_if_not_there2(e, rational(0))->get_data().m_value += mul; - } - } - unsigned num_negative = 0; - unsigned num_positive = 0; - bool is_unit_pos = true, is_unit_neg = true; - obj_map::iterator it = m_coeff_map.begin(); - obj_map::iterator end = m_coeff_map.end(); - for (; it != end; ++it) { - rational r = it->m_value; - if (r.is_zero()) { - continue; - } - m_terms.push_back(std::make_pair(it->m_key, r)); - if (r.is_pos()) { - is_unit_pos = is_unit_pos && r.is_one(); - num_positive++; - } - else { - is_unit_neg = is_unit_neg && r.is_minus_one(); - num_negative++; - } - } - if (num_negative <= 1 && is_unit_pos && is_unit_neg && num_positive <= 1) { - return diff; - } - if (num_positive <= 1 && is_unit_pos) { - return horn; - } - if (num_negative <= 1 && is_unit_neg) { - return co_horn; - } - return non_horn; - } - - -} diff --git a/src/smt/theory_horn_ineq.h b/src/smt/theory_horn_ineq.h deleted file mode 100644 index ed96f25f6..000000000 --- a/src/smt/theory_horn_ineq.h +++ /dev/null @@ -1,328 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - theory_horn_ineq.h - -Abstract: - - A*x <= weight + D*x, coefficients to A and D are non-negative, - D is a diagonal matrix. - Coefficients to weight may have both signs. - - Label variables by weight. - Select inequality that is not satisfied. - Set delta(LHS) := 0 - Set delta(RHS(x)) := weight(x) - b - Propagate weight increment through inequalities. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-04-18 - -Revision History: - - The implementaton is derived from theory_diff_logic. - ---*/ - -#ifndef _THEORY_HORN_INEQ_H_ -#define _THEORY_HORN_INEQ_H_ - -#include"rational.h" -#include"inf_rational.h" -#include"inf_int_rational.h" -#include"inf_eps_rational.h" -#include"smt_theory.h" -#include"arith_decl_plugin.h" -#include"smt_justification.h" -#include"map.h" -#include"smt_params.h" -#include"arith_eq_adapter.h" -#include"smt_model_generator.h" -#include"numeral_factory.h" -#include"smt_clause.h" - -namespace smt { - - class horn_ineq_tester { - ast_manager& m; - arith_util a; - ptr_vector m_todo; - svector m_pols; - ast_mark pos_mark, neg_mark; - obj_map m_coeff_map; - rational m_weight; - vector > m_terms; - - public: - enum classify_t { - co_horn, - horn, - diff, - non_horn - }; - horn_ineq_tester(ast_manager& m); - - // test if formula is in the Horn inequality fragment: - bool operator()(expr* fml); - bool operator()(unsigned num_fmls, expr* const* fmls); - - // linearize inequality/equality - classify_t linearize(expr* e); - classify_t linearize(expr* e1, expr* e2); - - // retrieve linearization - vector > const& get_linearization() const; - rational const& get_weight() const { return m_weight; } - private: - bool test_expr(lbool p, expr* e); - classify_t linearize(); - }; - - template - class theory_horn_ineq : public theory, private Ext { - - typedef typename Ext::numeral numeral; - typedef typename Ext::inf_numeral inf_numeral; - typedef literal explanation; - typedef theory_var th_var; - typedef svector th_var_vector; - typedef unsigned clause_id; - typedef vector > coeffs; - - class clause; - class graph; - class assignment_trail; - class parent_trail; - - class atom { - protected: - bool_var m_bvar; - bool m_true; - int m_pos; - int m_neg; - public: - atom(bool_var bv, int pos, int neg) : - m_bvar(bv), m_true(false), - m_pos(pos), m_neg(neg) {} - ~atom() {} - bool_var get_bool_var() const { return m_bvar; } - bool is_true() const { return m_true; } - void assign_eh(bool is_true) { m_true = is_true; } - int get_asserted_edge() const { return this->m_true?m_pos:m_neg; } - int get_pos() const { return m_pos; } - int get_neg() const { return m_neg; } - std::ostream& display(theory_horn_ineq const& th, std::ostream& out) const; - }; - typedef svector atoms; - - struct scope { - unsigned m_atoms_lim; - unsigned m_asserted_atoms_lim; - unsigned m_asserted_qhead_old; - }; - - struct stats { - unsigned m_num_conflicts; - unsigned m_num_assertions; - unsigned m_num_core2th_eqs; - unsigned m_num_core2th_diseqs; - - void reset() { - memset(this, 0, sizeof(*this)); - } - - stats() { - reset(); - } - }; - - stats m_stats; - smt_params m_params; - arith_util a; - arith_eq_adapter m_arith_eq_adapter; - th_var m_zero_int; // cache the variable representing the zero variable. - th_var m_zero_real; // cache the variable representing the zero variable. - - graph* m_graph; - atoms m_atoms; - unsigned_vector m_asserted_atoms; // set of asserted atoms - unsigned m_asserted_qhead; - u_map m_bool_var2atom; - svector m_scopes; - - double m_agility; - bool m_lia; - bool m_lra; - bool m_non_horn_ineq_exprs; - - horn_ineq_tester m_test; - - - arith_factory * m_factory; - rational m_delta; - rational m_lambda; - - - // Set a conflict due to a negative cycle. - void set_neg_cycle_conflict(); - - // Create a new theory variable. - virtual th_var mk_var(enode* n); - - virtual th_var mk_var(expr* n); - - void compute_delta(); - - void found_non_horn_ineq_expr(expr * n); - - bool is_interpreted(app* n) const { - return n->get_family_id() == get_family_id(); - } - - public: - theory_horn_ineq(ast_manager& m); - - virtual ~theory_horn_ineq(); - - virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_horn_ineq, get_manager()); } - - virtual char const * get_name() const { return "horn-inequality-logic"; } - - /** - \brief See comment in theory::mk_eq_atom - */ - virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } - - virtual void init(context * ctx); - - virtual bool internalize_atom(app * atom, bool gate_ctx); - - virtual bool internalize_term(app * term); - - virtual void internalize_eq_eh(app * atom, bool_var v); - - virtual void assign_eh(bool_var v, bool is_true); - - virtual void new_eq_eh(th_var v1, th_var v2) { - m_arith_eq_adapter.new_eq_eh(v1, v2); - } - - virtual bool use_diseqs() const { return true; } - - virtual void new_diseq_eh(th_var v1, th_var v2) { - m_arith_eq_adapter.new_diseq_eh(v1, v2); - } - - virtual void push_scope_eh(); - - virtual void pop_scope_eh(unsigned num_scopes); - - virtual void restart_eh() { - m_arith_eq_adapter.restart_eh(); - } - - virtual void relevant_eh(app* e) {} - - virtual void init_search_eh() { - m_arith_eq_adapter.init_search_eh(); - } - - virtual final_check_status final_check_eh(); - - virtual bool is_shared(th_var v) const { - return false; - } - - virtual bool can_propagate() { - SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); - return m_asserted_qhead != m_asserted_atoms.size(); - } - - virtual void propagate(); - - virtual justification * why_is_diseq(th_var v1, th_var v2) { - UNREACHABLE(); - return 0; - } - - virtual void reset_eh(); - - virtual void init_model(model_generator & m); - - virtual model_value_proc * mk_value(enode * n, model_generator & mg); - - virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { - return true; - } - - virtual void display(std::ostream & out) const; - - virtual void collect_statistics(::statistics & st) const; - - private: - - virtual void new_eq_eh(th_var v1, th_var v2, justification& j) { - m_stats.m_num_core2th_eqs++; - new_eq_or_diseq(true, v1, v2, j); - } - - virtual void new_diseq_eh(th_var v1, th_var v2, justification& j) { - m_stats.m_num_core2th_diseqs++; - new_eq_or_diseq(false, v1, v2, j); - } - - void negate(coeffs& coeffs, rational& weight); - numeral mk_weight(bool is_real, bool is_strict, rational const& w) const; - void mk_coeffs(vector >const& terms, coeffs& coeffs, rational& w); - - void del_atoms(unsigned old_size); - - void propagate_core(); - - bool propagate_atom(atom const& a); - - th_var mk_term(app* n); - - th_var mk_num(app* n, rational const& r); - - bool is_consistent() const; - - th_var expand(bool pos, th_var v, rational & k); - - void new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just); - - th_var get_zero(sort* s) const { return a.is_int(s)?m_zero_int:m_zero_real; } - - th_var get_zero(expr* e) const { return get_zero(get_manager().get_sort(e)); } - - void inc_conflicts(); - - }; - - struct rhi_ext { - typedef inf_rational inf_numeral; - typedef inf_eps_rational numeral; - numeral m_epsilon; - numeral m_minus_infty; - rhi_ext() : m_epsilon(inf_rational(rational(), true)), m_minus_infty(rational(-1),inf_rational()) {} - }; - - struct ihi_ext { - typedef rational inf_numeral; - typedef inf_eps_rational numeral; - numeral m_epsilon; - numeral m_minus_infty; - ihi_ext() : m_epsilon(rational(1)), m_minus_infty(rational(-1),rational(0)) {} - }; - - typedef theory_horn_ineq theory_rhi; - typedef theory_horn_ineq theory_ihi; -}; - - - - -#endif /* _THEORY_HORN_INEQ_H_ */ diff --git a/src/smt/theory_horn_ineq_def.h b/src/smt/theory_horn_ineq_def.h deleted file mode 100644 index f4fa7e8d7..000000000 --- a/src/smt/theory_horn_ineq_def.h +++ /dev/null @@ -1,1166 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - theory_horn_ineq_def.h - -Abstract: - - A*x <= b + D*x, coefficients to A and D are non-negative, - D is a diagonal matrix. - Coefficients to b may have both signs. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-04-18 - -Revision History: - ---*/ - -#ifndef _THEORY_HORN_INEQ_DEF_H_ -#define _THEORY_HORN_INEQ_DEF_H_ -#include "theory_horn_ineq.h" -#include "ast_pp.h" -#include "smt_context.h" - -namespace smt { - - static const unsigned null_clause_id = UINT_MAX; - - /** - A clause represents an inequality of the form - - v1*c1 + v2*c2 + .. + v_n*c_n + w <= v*c - - where - - m_vars: [v1,v2,...,v_n] - - m_coeffs: [c1,c2,...,c_n] - - m_var: v - - m_coeff: c - - m_weight: w - - */ - template - class theory_horn_ineq::clause { - vector m_coeffs; // coefficients of body. - svector m_vars; // variables of body. - rational m_coeff; // coefficient of head. - th_var m_var; // head variable. - numeral m_weight; // constant to add - literal m_explanation; - bool m_enabled; - public: - clause(unsigned sz, rational const* coeffs, th_var const* vars, - rational const& coeff, th_var var, numeral const& w, - const literal& ex): - m_coeffs(sz, coeffs), - m_vars(sz, vars), - m_coeff(coeff), - m_var(var), - m_weight(w), - m_explanation(ex), - m_enabled(false) { - DEBUG_CODE( - { - for (unsigned i = 0; i < size(); ++i) { - SASSERT(coeffs[i].is_pos()); - } - SASSERT(coeff.is_pos()); - }); - } - - th_var vars(unsigned i) const { return m_vars[i]; } - rational const& coeffs(unsigned i) const { return m_coeffs[i]; } - th_var var() const { return m_var; } - rational const& coeff() const { return m_coeff; } - const numeral & get_weight() const { return m_weight; } - const literal & get_explanation() const { return m_explanation; } - bool is_enabled() const { return m_enabled; } - unsigned size() const { return m_vars.size(); } - - void enable() { m_enabled = true; } - void disable() { m_enabled = false; } - - void display(std::ostream& out) const { - out << (is_enabled()?"+ ":"- "); - for (unsigned i = 0; i < size(); ++i) { - if (i > 0 && coeffs(i).is_pos()) { - out << " + "; - } - display(out, coeffs(i), vars(i)); - } - if (!get_weight().is_zero()) { - out << " + " << get_weight(); - } - display(out << " <= ", coeff(), var()); - out << "\n"; - } - - private: - - void display(std::ostream& out, rational const& c, th_var v) const { - if (!c.is_one()) { - out << c << "*"; - } - out << "v" << v; - } - }; - - template - class theory_horn_ineq::assignment_trail { - th_var m_var; - numeral m_old_value; - public: - assignment_trail(th_var v, const numeral & val): - m_var(v), - m_old_value(val) {} - th_var get_var() const { return m_var; } - const numeral & get_old_value() const { return m_old_value; } - }; - - template - class theory_horn_ineq::parent_trail { - th_var m_var; - clause_id m_old_value; - public: - parent_trail(th_var v, clause_id val): - m_var(v), - m_old_value(val) {} - th_var get_var() const { return m_var; } - clause_id get_old_value() const { return m_old_value; } - }; - - - template - class theory_horn_ineq::graph : private Ext { - - typedef vector assignment_stack; - typedef vector parent_stack; - typedef unsigned_vector clause_id_vector; - - struct stats { - unsigned m_propagation_cost; - - void reset() { - memset(this, 0, sizeof(*this)); - } - }; - - struct scope { - unsigned m_clauses_lim; - unsigned m_enabled_clauses_lim; - unsigned m_assignment_lim; - unsigned m_parent_lim; - scope(unsigned e, unsigned enabled, unsigned alim, unsigned plim): - m_clauses_lim(e), - m_enabled_clauses_lim(enabled), - m_assignment_lim(alim), - m_parent_lim(plim) { - } - }; - - stats m_stats; - vector m_clauses; - vector m_assignment; // per var - clause_id_vector m_parent; // per var - assignment_stack m_assignment_stack; // stack for restoring the assignment - parent_stack m_parent_stack; // stack for restoring parents - clause_id_vector m_enabled_clauses; - vector m_out_clauses; // use-list for clauses. - vector m_in_clauses; // clauses that have variable in head. - // forward reachability - unsigned_vector m_onstack; - unsigned m_ts; - unsigned_vector m_todo; - literal_vector m_lits; - vector m_coeffs; - th_var m_zero; - clause_id m_unsat_clause; - svector m_trail_stack; - - - public: - - graph(): m_ts(0), m_zero(null_theory_var), m_unsat_clause(null_clause_id) {} - - void reset() { - m_clauses .reset(); - m_assignment .reset(); - m_parent .reset(); - m_assignment_stack .reset(); - m_parent_stack .reset(); - m_out_clauses .reset(); - m_in_clauses .reset(); - m_enabled_clauses .reset(); - m_onstack .reset(); - m_ts = 0; - m_lits .reset(); - m_trail_stack .reset(); - m_unsat_clause = null_clause_id; - } - - - void traverse_neg_cycle1(bool /*stronger_lemmas*/) { - TRACE("horn_ineq", display(tout);); - SASSERT(!m_enabled_clauses.empty()); - clause_id id = m_unsat_clause; - SASSERT(id != null_clause_id); - SASSERT(!is_feasible(m_clauses[id])); - clause_id_vector todo; - vector muls; - todo.push_back(id); - muls.push_back(rational(1)); - u_map lits; - while (!todo.empty()) { - id = todo.back(); - rational mul = muls.back(); - todo.pop_back(); - muls.pop_back(); - clause const& cl = m_clauses[id]; - literal lit = cl.get_explanation(); - if (lit != null_literal) { - lits.insert_if_not_there2(id, rational(0))->get_data().m_value += mul; - } - for (unsigned i = 0; i < cl.size(); ++i) { - id = m_parent[cl.vars(i)]; - if (id != null_clause_id) { - todo.push_back(id); - muls.push_back(mul*cl.coeffs(i)); - } - } - } - u_map::iterator it = lits.begin(), end = lits.end(); - m_lits.reset(); - m_coeffs.reset(); - for (; it != end; ++it) { - m_lits.push_back(m_clauses[it->m_key].get_explanation()); - m_coeffs.push_back(it->m_value); - } - - // TODO: use topological sort to tune traversal of parents to linear. - // (current traversal can be exponential). - // TODO: negative cycle traversal with inline resolution to find - // stronger conflict clauses. - // follow heuristic used in theory_diff_logic_def.h: - } - - unsigned get_num_clauses() const { - return m_clauses.size(); - } - - literal_vector const& get_lits() const { - return m_lits; - } - - vector const& get_coeffs() const { - return m_coeffs; - } - - numeral get_assignment(th_var v) const { - return m_assignment[v]; - } - - numeral eval_body(clause const& cl) const { - numeral v(0); - for (unsigned i = 0; i < cl.size(); ++i) { - v += cl.coeffs(i)*m_assignment[cl.vars(i)]; - } - v += cl.get_weight(); - return v; - } - - numeral eval_body(clause_id id) const { - return eval_body(m_clauses[id]); - } - - numeral eval_head(clause_id id) const { - return eval_head(m_clauses[id]); - } - - numeral eval_head(clause const& cl) const { - return cl.coeff()*m_assignment[cl.var()]; - } - - clause const& get_clause(clause_id id) const { - return m_clauses[id]; - } - - void display_clause(std::ostream& out, clause_id id) const { - if (id == null_clause_id) { - out << "null\n"; - } - else { - m_clauses[id].display(out); - } - } - - void display(std::ostream& out) const { - for (unsigned i = 0; i < m_clauses.size(); ++i) { - display_clause(out, i); - } - for (unsigned i = 0; i < m_assignment.size(); ++i) { - out << m_assignment[i] << "\n"; - } - } - - void collect_statistics(::statistics& st) const { - st.update("hi_propagation_cst", m_stats.m_propagation_cost); - } - - void push() { - m_trail_stack.push_back(scope(m_clauses.size(), m_enabled_clauses.size(), - m_assignment_stack.size(), m_parent_stack.size())); - } - - void pop(unsigned num_scopes) { - unsigned lvl = m_trail_stack.size(); - SASSERT(num_scopes <= lvl); - unsigned new_lvl = lvl - num_scopes; - scope & s = m_trail_stack[new_lvl]; - // restore enabled clauses - for (unsigned i = m_enabled_clauses.size(); i > s.m_enabled_clauses_lim; ) { - --i; - m_clauses[m_enabled_clauses[i]].disable(); - } - m_enabled_clauses.shrink(s.m_enabled_clauses_lim); - - // restore assignment stack - for (unsigned i = m_assignment_stack.size(); i > s.m_assignment_lim; ) { - --i; - m_assignment[m_assignment_stack[i].get_var()] = m_assignment_stack[i].get_old_value(); - } - m_assignment_stack.shrink(s.m_assignment_lim); - - // restore parent stack - for (unsigned i = m_parent_stack.size(); i > s.m_parent_lim; ) { - --i; - m_parent[m_parent_stack[i].get_var()] = m_parent_stack[i].get_old_value(); - } - m_assignment_stack.shrink(s.m_assignment_lim); - - // restore clauses - unsigned old_num_clauses = s.m_clauses_lim; - unsigned num_clauses = m_clauses.size(); - SASSERT(old_num_clauses <= num_clauses); - unsigned to_delete = num_clauses - old_num_clauses; - for (unsigned i = 0; i < to_delete; i++) { - const clause & cl = m_clauses.back(); - TRACE("horn_ineq", tout << "deleting clause:\n"; cl.display(tout);); - for (unsigned j = 0; j < cl.size(); ++j) { - m_out_clauses[cl.vars(j)].pop_back(); - } - m_in_clauses[cl.var()].pop_back(); - m_clauses.pop_back(); - } - m_trail_stack.shrink(new_lvl); - SASSERT(check_invariant()); - } - - /** - \brief Add clause z <= z and the assignment z := 0 - Then z cannot be incremented without causing a loop (and therefore a contradiction). - */ - void set_to_zero(th_var z) { - m_zero = z; - } - - bool enable_clause(clause_id id) { - if (id == null_clause_id) { - return true; - } - clause& cl = m_clauses[id]; - bool r = true; - if (!cl.is_enabled()) { - cl.enable(); - if (!is_feasible(cl)) { - r = make_feasible(id); - } - m_enabled_clauses.push_back(id); - } - return r; - } - - void init_var(th_var v) { - unsigned sz = static_cast(v); - while (m_assignment.size() <= sz) { - m_assignment.push_back(Ext::m_minus_infty); - m_out_clauses.push_back(clause_id_vector()); - m_in_clauses.push_back(clause_id_vector()); - m_parent.push_back(null_clause_id); - m_onstack.push_back(0); - } - m_assignment[v] = Ext::m_minus_infty; - SASSERT(m_out_clauses[v].empty()); - SASSERT(m_in_clauses[v].empty()); - SASSERT(check_invariant()); - } - - clause_id add_ineq(vector > const& terms, numeral const& weight, literal l) { - vector coeffs; - svector vars; - rational coeff(1); - th_var var = null_theory_var; - bool found_negative = false; - for (unsigned i = 0; i < terms.size(); ++i) { - rational const& r = terms[i].second; - if (r.is_pos()) { - coeffs.push_back(terms[i].second); - vars.push_back(terms[i].first); - } - else if (found_negative) { - return null_clause_id; - } - else { - SASSERT(r.is_neg()); - found_negative = true; - coeff = -r; - var = terms[i].first; - } - } - if (!found_negative) { - coeff = rational(1); - var = m_zero; - } - if (!coeff.is_one()) { - // so far just support unit coefficients on right. - return null_clause_id; - } - clause_id id = m_clauses.size(); - m_clauses.push_back(clause(coeffs.size(), coeffs.c_ptr(), vars.c_ptr(), coeff, var, weight, l)); - for (unsigned i = 0; i < vars.size(); ++i) { - m_out_clauses[vars[i]].push_back(id); - } - m_in_clauses[var].push_back(id); - - return id; - } - - bool is_feasible() const { - for (unsigned i = 0; i < m_clauses.size(); ++i) { - if (!is_feasible(m_clauses[i])) { - return false; - } - } - return true; - } - - private: - - bool check_invariant() { - return true; - } - - /** - assignments are fully retraced on backtracking. - This is not always necessary. - */ - - void acc_assignment(th_var v, const numeral & inc) { - m_assignment_stack.push_back(assignment_trail(v, m_assignment[v])); - m_assignment[v] += inc; - } - - void acc_parent(th_var v, clause_id parent) { - m_parent[v] = parent; - m_parent_stack.push_back(parent_trail(v, parent)); - } - - numeral get_delta(const clause & cl) const { - SASSERT(cl.coeff().is_one() && "Not yet support for non-units"); - return eval_body(cl) - eval_head(cl); - } - - void set_onstack(th_var v) { - SASSERT(m_ts != 0); - m_onstack[v] = m_ts; - } - - void reset_onstack(th_var v) { - m_onstack[v] = 0; - } - - bool is_onstack(th_var v) const { - return m_onstack[v] == m_ts; - } - - void inc_ts() { - m_ts++; - if (m_ts == 0) { - m_ts++; - m_onstack.reset(); - m_onstack.resize(m_assignment.size(), 0); - } - } - - // Make the assignment feasible. An assignment is feasible if - // Forall clause cl. eval_body(cl) <= eval_head(cl) - // - // This method assumes that if the assignment is not feasible, - // then the only infeasible clause is the last added clause. - // - // Traversal is by naive DFS search. - // - bool make_feasible(clause_id id) { - SASSERT(is_almost_feasible(id)); - SASSERT(!m_clauses.empty()); - SASSERT(!is_feasible(m_clauses[id])); - const clause & cl0 = m_clauses[id]; - inc_ts(); - for (unsigned i = 0; i < cl0.size(); ++i) { - set_onstack(cl0.vars(i)); - } - th_var source = cl0.var(); - numeral delta = get_delta(cl0); - acc_parent(source, id); - SASSERT(delta.is_pos()); - acc_assignment(source, delta); - m_todo.reset(); - m_todo.push_back(source); - - TRACE("horn_ineq", cl0.display(tout);); - - do { - ++m_stats.m_propagation_cost; - - typename clause_id_vector::iterator it = m_out_clauses[source].begin(); - typename clause_id_vector::iterator end = m_out_clauses[source].end(); - for (; it != end; ++it) { - clause & cl = m_clauses[*it]; - if (!cl.is_enabled()) { - continue; - } - delta = get_delta(cl); - - if (delta.is_pos()) { - TRACE("horn_ineq", cl.display(tout);); - th_var target = cl.var(); - if (is_onstack(target)) { - m_unsat_clause = *it; - return false; - } - else { - acc_assignment(target, delta); - acc_parent(target, *it); - m_todo.push_back(target); - } - } - } - set_onstack(source); - source = m_todo.back(); - // pop stack until there is a new variable to process. - while (is_onstack(source)) { - m_todo.pop_back(); - reset_onstack(source); - if (m_todo.empty()) { - break; - } - source = m_todo.back(); - } - } - while (!m_todo.empty()); - return true; - } - - bool is_almost_feasible(clause_id id) const { - for (unsigned i = 0; i < m_clauses.size(); ++i) { - if (id != static_cast(i) && !is_feasible(m_clauses[i])) { - return false; - } - } - return true; - } - - bool is_feasible(const clause & cl) const { - return !cl.is_enabled() || get_delta(cl).is_nonpos(); - } - - }; - - template - theory_horn_ineq::theory_horn_ineq(ast_manager& m): - theory(m.mk_family_id("arith")), - a(m), - m_arith_eq_adapter(*this, m_params, a), - m_zero_int(null_theory_var), - m_zero_real(null_theory_var), - m_graph(0), - m_asserted_qhead(0), - m_agility(0.5), - m_lia(false), - m_lra(false), - m_non_horn_ineq_exprs(false), - m_test(m), - m_factory(0) { - m_graph = alloc(graph); - } - - template - theory_horn_ineq::~theory_horn_ineq() { - reset_eh(); - dealloc(m_graph); - } - - template - std::ostream& theory_horn_ineq::atom::display(theory_horn_ineq const& th, std::ostream& out) const { - context& ctx = th.get_context(); - lbool asgn = ctx.get_assignment(m_bvar); - bool sign = (l_undef == asgn) || m_true; - return out << literal(m_bvar, sign) << " " << mk_pp(ctx.bool_var2expr(m_bvar), th.get_manager()) << " "; - if (l_undef == asgn) { - out << "unassigned\n"; - } - else { - th.m_graph->display_clause(out, get_asserted_edge()); - } - return out; - } - - template - theory_var theory_horn_ineq::mk_var(enode* n) { - th_var v = theory::mk_var(n); - m_graph->init_var(v); - get_context().attach_th_var(n, this, v); - return v; - } - - template - theory_var theory_horn_ineq::mk_var(expr* n) { - context & ctx = get_context(); - enode* e = 0; - th_var v = null_theory_var; - m_lia |= a.is_int(n); - m_lra |= a.is_real(n); - if (!is_app(n)) { - return v; - } - if (ctx.e_internalized(n)) { - e = ctx.get_enode(n); - v = e->get_th_var(get_id()); - } - else { - ctx.internalize(n, false); - e = ctx.get_enode(n); - } - if (v == null_theory_var) { - v = mk_var(e); - } - if (is_interpreted(to_app(n))) { - found_non_horn_ineq_expr(n); - } - return v; - } - - template - void theory_horn_ineq::reset_eh() { - m_graph ->reset(); - m_zero_int = null_theory_var; - m_zero_real = null_theory_var; - m_atoms .reset(); - m_asserted_atoms .reset(); - m_stats .reset(); - m_scopes .reset(); - m_asserted_qhead = 0; - m_agility = 0.5; - m_lia = false; - m_lra = false; - m_non_horn_ineq_exprs = false; - theory::reset_eh(); - } - - - - template - void theory_horn_ineq::new_eq_or_diseq(bool is_eq, th_var v1, th_var v2, justification& eq_just) { - rational k; - th_var s = expand(true, v1, k); - th_var t = expand(false, v2, k); - context& ctx = get_context(); - ast_manager& m = get_manager(); - - if (s == t) { - if (is_eq != k.is_zero()) { - // conflict 0 /= k; - inc_conflicts(); - ctx.set_conflict(&eq_just); - } - } - else { - // - // Create equality ast, internalize_atom - // assign the corresponding equality literal. - // - - app_ref eq(m), s2(m), t2(m); - app* s1 = get_enode(s)->get_owner(); - app* t1 = get_enode(t)->get_owner(); - s2 = a.mk_sub(t1, s1); - t2 = a.mk_numeral(k, m.get_sort(s2.get())); - // t1 - s1 = k - eq = m.mk_eq(s2.get(), t2.get()); - - TRACE("horn_ineq", - tout << v1 << " .. " << v2 << "\n"; - tout << mk_pp(eq.get(), m) <<"\n";); - - if (!internalize_atom(eq.get(), false)) { - UNREACHABLE(); - } - - literal l(ctx.get_literal(eq.get())); - if (!is_eq) { - l = ~l; - } - - ctx.assign(l, b_justification(&eq_just), false); - } - } - - template - void theory_horn_ineq::inc_conflicts() { - m_stats.m_num_conflicts++; - if (m_params.m_arith_adaptive) { - double g = m_params.m_arith_adaptive_propagation_threshold; - m_agility = m_agility*g + 1 - g; - } - } - - template - void theory_horn_ineq::set_neg_cycle_conflict() { - m_graph->traverse_neg_cycle1(m_params.m_arith_stronger_lemmas); - inc_conflicts(); - literal_vector const& lits = m_graph->get_lits(); - context & ctx = get_context(); - TRACE("horn_ineq", - tout << "conflict: "; - for (unsigned i = 0; i < lits.size(); ++i) { - ctx.display_literal_info(tout, lits[i]); - } - tout << "\n"; - ); - - if (m_params.m_arith_dump_lemmas) { - char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; - ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); - } - - vector params; - if (get_manager().proofs_enabled()) { - params.push_back(parameter(symbol("farkas"))); - vector const& coeffs = m_graph->get_coeffs(); - for (unsigned i = 0; i < coeffs.size(); ++i) { - params.push_back(parameter(coeffs[i])); - } - } - - ctx.set_conflict( - ctx.mk_justification( - ext_theory_conflict_justification( - get_id(), ctx.get_region(), - lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); - } - - template - void theory_horn_ineq::found_non_horn_ineq_expr(expr* n) { - if (!m_non_horn_ineq_exprs) { - TRACE("horn_ineq", tout << "found non horn logic expression:\n" << mk_pp(n, get_manager()) << "\n";); - get_context().push_trail(value_trail(m_non_horn_ineq_exprs)); - m_non_horn_ineq_exprs = true; - } - } - - template - void theory_horn_ineq::init(context* ctx) { - theory::init(ctx); - m_zero_int = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), true), false, false, true)); - m_zero_real = mk_var(ctx->mk_enode(a.mk_numeral(rational(0), false), false, false, true)); - m_graph->set_to_zero(m_zero_int); - m_graph->set_to_zero(m_zero_real); - } - - /** - \brief Create negated literal. - - The negation of: E <= 0 - - -E + epsilon <= 0 - or - -E + 1 <= 0 - */ - template - void theory_horn_ineq::negate(coeffs& coeffs, rational& weight) { - for (unsigned i = 0; i < coeffs.size(); ++i) { - coeffs[i].second.neg(); - } - weight.neg(); - } - - template - typename theory_horn_ineq::numeral theory_horn_ineq::mk_weight(bool is_real, bool is_strict, rational const& w) const { - if (is_strict) { - return numeral(inf_numeral(w)) + (is_real?Ext::m_epsilon:numeral(1)); - } - else { - return numeral(inf_numeral(w)); - } - } - - template - void theory_horn_ineq::mk_coeffs(vector > const& terms, coeffs& coeffs, rational& w) { - coeffs.reset(); - w = m_test.get_weight(); - for (unsigned i = 0; i < terms.size(); ++i) { - coeffs.push_back(std::make_pair(mk_var(terms[i].first), terms[i].second)); - } - } - - template - bool theory_horn_ineq::internalize_atom(app * n, bool) { - context & ctx = get_context(); - if (!a.is_le(n) && !a.is_ge(n) && !a.is_lt(n) && !a.is_gt(n)) { - found_non_horn_ineq_expr(n); - return false; - } - SASSERT(!ctx.b_internalized(n)); - expr* e1 = n->get_arg(0), *e2 = n->get_arg(1); - if (a.is_ge(n) || a.is_gt(n)) { - std::swap(e1, e2); - } - bool is_strict = a.is_gt(n) || a.is_lt(n); - - horn_ineq_tester::classify_t cl = m_test.linearize(e1, e2); - if (cl == horn_ineq_tester::non_horn) { - found_non_horn_ineq_expr(n); - return false; - } - - rational w; - coeffs coeffs; - mk_coeffs(m_test.get_linearization(), coeffs, w); - bool_var bv = ctx.mk_bool_var(n); - ctx.set_var_theory(bv, get_id()); - literal l(bv); - numeral w1 = mk_weight(a.is_real(e1), is_strict, w); - clause_id pos = m_graph->add_ineq(coeffs, w1, l); - negate(coeffs, w); - numeral w2 = mk_weight(a.is_real(e1), !is_strict, w); - clause_id neg = m_graph->add_ineq(coeffs, w2, ~l); - m_bool_var2atom.insert(bv, m_atoms.size()); - m_atoms.push_back(atom(bv, pos, neg)); - - TRACE("horn_ineq", - tout << mk_pp(n, get_manager()) << "\n"; - m_graph->display_clause(tout << "pos: ", pos); - m_graph->display_clause(tout << "neg: ", neg); - ); - - return true; - } - - template - bool theory_horn_ineq::internalize_term(app * term) { - bool result = null_theory_var != mk_term(term); - CTRACE("horn_ineq", !result, tout << "Did not internalize " << mk_pp(term, get_manager()) << "\n";); - TRACE("horn_ineq", tout << "Terms may not be internalized " << mk_pp(term, get_manager()) << "\n";); - found_non_horn_ineq_expr(term); - return result; - } - - template - void theory_horn_ineq::internalize_eq_eh(app * atom, bool_var) { - // noop - } - - template - void theory_horn_ineq::assign_eh(bool_var v, bool is_true) { - m_stats.m_num_assertions++; - unsigned idx = m_bool_var2atom.find(v); - SASSERT(get_context().get_assignment(v) != l_undef); - SASSERT((get_context().get_assignment(v) == l_true) == is_true); - m_atoms[idx].assign_eh(is_true); - m_asserted_atoms.push_back(idx); - } - - template - void theory_horn_ineq::push_scope_eh() { - theory::push_scope_eh(); - m_graph->push(); - m_scopes.push_back(scope()); - scope & s = m_scopes.back(); - s.m_atoms_lim = m_atoms.size(); - s.m_asserted_atoms_lim = m_asserted_atoms.size(); - s.m_asserted_qhead_old = m_asserted_qhead; - } - - template - void theory_horn_ineq::pop_scope_eh(unsigned num_scopes) { - unsigned lvl = m_scopes.size(); - SASSERT(num_scopes <= lvl); - unsigned new_lvl = lvl - num_scopes; - scope & s = m_scopes[new_lvl]; - del_atoms(s.m_atoms_lim); - 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); - theory::pop_scope_eh(num_scopes); - } - - template - final_check_status theory_horn_ineq::final_check_eh() { - SASSERT(is_consistent()); - TRACE("horn_ineq", display(tout);); - if (can_propagate()) { - propagate_core(); - return FC_CONTINUE; - } - else if (m_non_horn_ineq_exprs) { - return FC_GIVEUP; - } - else { - return FC_DONE; - } - } - - template - void theory_horn_ineq::propagate() { - propagate_core(); - } - - template - void theory_horn_ineq::display(std::ostream& out) const { - for (unsigned i = 0; i < m_atoms.size(); ++i) { - m_atoms[i].display(*this, out); - } - out << "\n"; - m_graph->display(out); - } - - template - void theory_horn_ineq::collect_statistics(::statistics& st) const { - st.update("horn ineq conflicts", m_stats.m_num_conflicts); - st.update("horn ineq assertions", m_stats.m_num_assertions); - m_arith_eq_adapter.collect_statistics(st); - m_graph->collect_statistics(st); - } - - template - void theory_horn_ineq::del_atoms(unsigned old_size) { - typename atoms::iterator begin = m_atoms.begin() + old_size; - typename atoms::iterator it = m_atoms.end(); - while (it != begin) { - --it; - m_bool_var2atom.erase(it->get_bool_var()); - } - m_atoms.shrink(old_size); - } - - template - void theory_horn_ineq::propagate_core() { - bool consistent = true; - while (consistent && can_propagate()) { - unsigned idx = m_asserted_atoms[m_asserted_qhead]; - m_asserted_qhead++; - consistent = propagate_atom(m_atoms[idx]); - } - } - - template - bool theory_horn_ineq::propagate_atom(atom const& a) { - context& ctx = get_context(); - TRACE("horn_ineq", a.display(*this, tout); tout << "\n";); - if (ctx.inconsistent()) { - return false; - } - int clause_id = a.get_asserted_edge(); - if (!m_graph->enable_clause(clause_id)) { - set_neg_cycle_conflict(); - return false; - } - return true; - } - - template - theory_var theory_horn_ineq::mk_term(app* n) { - context& ctx = get_context(); - - horn_ineq_tester::classify_t cl = m_test.linearize(n); - if (cl == horn_ineq_tester::non_horn) { - found_non_horn_ineq_expr(n); - return null_theory_var; - } - - coeffs coeffs; - rational w; - mk_coeffs(m_test.get_linearization(), coeffs, w); - if (coeffs.empty()) { - return mk_num(n, w); - } - if (coeffs.size() == 1 && coeffs[0].second.is_one()) { - return coeffs[0].first; - } - th_var target = mk_var(ctx.mk_enode(n, false, false, true)); - coeffs.push_back(std::make_pair(target, rational(-1))); - - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); - negate(coeffs, w); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(w)), null_literal))); - return target; - } - - template - theory_var theory_horn_ineq::mk_num(app* n, rational const& r) { - theory_var v = null_theory_var; - context& ctx = get_context(); - if (r.is_zero()) { - v = a.is_int(n)?m_zero_int:m_zero_real; - } - else if (ctx.e_internalized(n)) { - enode* e = ctx.get_enode(n); - v = e->get_th_var(get_id()); - SASSERT(v != null_theory_var); - } - else { - v = mk_var(ctx.mk_enode(n, false, false, true)); - // v = k: v <= k k <= v - coeffs coeffs; - coeffs.push_back(std::make_pair(v, rational(1))); - VERIFY(m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(r)), null_literal))); - coeffs.back().second.neg(); - VERIFY (m_graph->enable_clause(m_graph->add_ineq(coeffs, numeral(inf_numeral(-r)), null_literal))); - } - return v; - } - - template - theory_var theory_horn_ineq::expand(bool pos, th_var v, rational & k) { - context& ctx = get_context(); - enode* e = get_enode(v); - expr* x, *y; - rational r; - for (;;) { - app* n = e->get_owner(); - if (a.is_add(n, x, y)) { - if (a.is_numeral(x, r)) { - e = ctx.get_enode(y); - } - else if (a.is_numeral(y, r)) { - e = ctx.get_enode(x); - } - v = e->get_th_var(get_id()); - SASSERT(v != null_theory_var); - if (v == null_theory_var) { - break; - } - if (pos) { - k += r; - } - else { - k -= r; - } - } - else { - break; - } - } - return v; - } - - template - bool theory_horn_ineq::is_consistent() const { - return m_graph->is_feasible(); - } - - // models: - template - void theory_horn_ineq::init_model(model_generator & m) { - m_factory = alloc(arith_factory, get_manager()); - m.register_factory(m_factory); - compute_delta(); - } - - template - model_value_proc * theory_horn_ineq::mk_value(enode * n, model_generator & mg) { - theory_var v = n->get_th_var(get_id()); - SASSERT(v != null_theory_var); - numeral val = m_graph->get_assignment(v); - rational num = val.get_infinity()*m_lambda + val.get_rational() + val.get_infinitesimal()*m_delta; - TRACE("horn_ineq", tout << mk_pp(n->get_owner(), get_manager()) << " |-> " << num << "\n";); - return alloc(expr_wrapper_proc, m_factory->mk_value(num, a.is_int(n->get_owner()))); - } - - /** - \brief Compute numeral values for the infinitesimals to satisfy the inequalities. - */ - - template - void theory_horn_ineq::compute_delta() { - m_delta = rational(1); - m_lambda = rational(0); - unsigned sz = m_graph->get_num_clauses(); - - for (unsigned i = 0; i < sz; ++i) { - if (!m_graph->get_clause(i).is_enabled()) { - continue; - } - numeral b = m_graph->eval_body(i); - numeral h = m_graph->eval_head(i); - - if (b.get_infinity() < h.get_infinity()) { - continue; - } - SASSERT(b.get_infinity() == h.get_infinity()); - - // b <= h - // suppose that h.eps < b.eps - // then we have h.num > b.num - // but also h.num + delta*h.eps >= b.num + delta*b.eps - // <=> - // (h.num - b.num)/(b.eps - h.eps) >= delta - rational num_r = h.get_rational() - b.get_rational(); - rational eps_r = b.get_infinitesimal() - h.get_infinitesimal(); - if (eps_r.is_pos()) { - SASSERT(num_r.is_pos()); - rational new_delta = num_r/eps_r; - if (new_delta < m_delta) { - m_delta = new_delta; - } - } - } - - for (unsigned i = 0; i < sz; ++i) { - if (!m_graph->get_clause(i).is_enabled()) { - continue; - } - numeral b = m_graph->eval_body(i); - numeral h = m_graph->eval_head(i); - - rational ir = h.get_infinity() - b.get_infinity(); - rational hr = b.get_rational() - h.get_rational(); - rational num_r = hr + m_delta*(b.get_infinitesimal() - h.get_infinitesimal()); - - SASSERT(b.get_infinity() <= h.get_infinity()); - - // b <= h - // suppose that h.finite < b.finite - // then we have h.infinite > b.infinite - // but also - // h.infinite*lambda + h.finite >= b.infinite*lambda + b.finite - // <=> - // lambda >= (b.finite - h.finite) / (h.infinite - b.infinite) - if (num_r.is_pos()) { - SASSERT(ir.is_pos()); - rational new_lambda = num_r/ir; - if (new_lambda > m_lambda) { - m_lambda = new_lambda; - } - } - } - } - - - -}; - -#endif From e27b7e30381a66ff033a0afa80fdd82c749447fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 1 Mar 2014 11:09:57 -0800 Subject: [PATCH 24/54] use size_t for return values from strlen Signed-off-by: Nikolaj Bjorner --- src/smt/smt_quantifier.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index 8fd0e08bc..e9b0e069a 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -439,12 +439,12 @@ namespace smt { virtual bool model_based() const { return m_fparams->m_mbqi; } virtual bool mbqi_enabled(quantifier *q) const { - if(!m_fparams->m_mbqi_id) return true; - const symbol &s = q->get_qid(); - unsigned len = strlen(m_fparams->m_mbqi_id); - if(s == symbol::null || s.is_numerical()) - return len == 0; - return strncmp(s.bare_str(),m_fparams->m_mbqi_id,len) == 0; + if(!m_fparams->m_mbqi_id) return true; + const symbol &s = q->get_qid(); + size_t len = strlen(m_fparams->m_mbqi_id); + if(s == symbol::null || s.is_numerical()) + return len == 0; + return strncmp(s.bare_str(),m_fparams->m_mbqi_id,len) == 0; } /* Quantifier id's must begin with the prefix specified by From a00a9fbdfd1e9545c26d8cb5114832dc13849114 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:10:48 -0800 Subject: [PATCH 25/54] generate error on duplicated data-type accessors. Issue 85 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/pdecl.cpp | 19 +++++++++++++++++++ src/cmd_context/pdecl.h | 2 ++ src/parsers/smt2/smt2parser.cpp | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 44ba2b4d2..4a51e4943 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -515,6 +515,25 @@ bool pdatatype_decl::has_missing_refs(symbol & missing) const { return false; } +bool pdatatype_decl::has_duplicate_accessors(symbol & duplicated) const { + hashtable names; + ptr_vector::const_iterator it = m_constructors.begin(); + ptr_vector::const_iterator end = m_constructors.end(); + for (; it != end; ++it) { + ptr_vector const& acc = (*it)->m_accessors; + for (unsigned i = 0; i < acc.size(); ++i) { + symbol const& name = acc[i]->get_name(); + if (names.contains(name)) { + duplicated = name; + return true; + } + names.insert(name); + } + } + return false; +} + + bool pdatatype_decl::fix_missing_refs(dictionary const & symbol2idx, symbol & missing) { ptr_vector::iterator it = m_constructors.begin(); ptr_vector::iterator end = m_constructors.end(); diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index 76ad2a020..83d881f57 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -169,6 +169,7 @@ public: class paccessor_decl : public pdecl { friend class pdecl_manager; friend class pconstructor_decl; + friend class pdatatype_decl; symbol m_name; ptype m_type; paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r); @@ -222,6 +223,7 @@ public: sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); virtual void display(std::ostream & out) const; bool has_missing_refs(symbol & missing) const; + bool has_duplicate_accessors(symbol & repeated) const; }; /** diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index b5af6353e..546fd3819 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -850,6 +850,13 @@ namespace smt2 { for (unsigned i = 0; i < sz; i++) { pdatatype_decl * d = new_dt_decls[i]; SASSERT(d != 0); + symbol duplicated; + if (d->has_duplicate_accessors(duplicated)) { + std::string err_msg = "invalid datatype declaration, repeated accessor identifier '"; + err_msg += duplicated.str(); + err_msg += "'"; + throw parser_exception(err_msg, line, pos); + } m_ctx.insert(d); if (d->get_num_params() == 0) { // if datatype is not parametric... then force instantiation to register accessor, recognizers and constructors... From 4f20216677199792f784ed4aa43f6927bbafb417 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:14:26 -0800 Subject: [PATCH 26/54] fix documnetation to say milli-seconds. Issue 84 Signed-off-by: Nikolaj Bjorner --- src/shell/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shell/main.cpp b/src/shell/main.cpp index e0fa4a1f2..f23bc470c 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -87,7 +87,7 @@ void display_usage() { std::cout << "\nResources:\n"; // timeout and memout are now available on Linux and OSX too. std::cout << " -T:timeout set the timeout (in seconds).\n"; - std::cout << " -t:timeout set the soft timeout (in seconds). It only kills the current query.\n"; + std::cout << " -t:timeout set the soft timeout (in milli seconds). It only kills the current query.\n"; std::cout << " -memory:Megabytes set a limit for virtual memory consumption.\n"; // std::cout << "\nOutput:\n"; From 23313e5bdca1bde6aee38c61eb4c46ffb4f2f8ff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:24:40 -0800 Subject: [PATCH 27/54] remove unsound simplification for rem. Codeplex Issue 76 Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/arith_rewriter.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 0856ab5b8..b040efc0c 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -753,12 +753,7 @@ br_status arith_rewriter::mk_rem_core(expr * arg1, expr * arg2, expr_ref & resul } else if (m_util.is_numeral(arg2, v2, is_int) && is_int && !v2.is_zero()) { if (is_add(arg1) || is_mul(arg1)) { - ptr_buffer new_args; - unsigned num_args = to_app(arg1)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - new_args.push_back(m_util.mk_rem(to_app(arg1)->get_arg(i), arg2)); - result = m().mk_app(to_app(arg1)->get_decl(), new_args.size(), new_args.c_ptr()); - return BR_REWRITE2; + return BR_FAILED; } else { if (v2.is_neg()) { From b1b349f49662d16c87060f82ddcf8204ce4f7b74 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 2 Mar 2014 17:50:29 -0800 Subject: [PATCH 28/54] modify offset check to accept linear expressions over numerals. Codeplex issue 81 Signed-off-by: Nikolaj Bjorner --- src/cmd_context/check_logic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cmd_context/check_logic.cpp b/src/cmd_context/check_logic.cpp index dfee148b5..99b0f2521 100644 --- a/src/cmd_context/check_logic.cpp +++ b/src/cmd_context/check_logic.cpp @@ -310,6 +310,8 @@ struct check_logic::imp { return false; non_numeral = arg; } + if (non_numeral == 0) + return true; if (is_diff_var(non_numeral)) return true; if (!m_a_util.is_add(non_numeral) && !m_a_util.is_sub(non_numeral)) From 601cb43f7898f108ae34aa6755d74f1ac2693ff6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 4 Mar 2014 17:18:49 -0800 Subject: [PATCH 29/54] fix quotation bug reported by Arie Gurfinkel Signed-off-by: Nikolaj Bjorner --- src/ast/ast_smt2_pp.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 1ad2b8222..77c8ac58f 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -472,6 +472,25 @@ class smt2_printer { ast_manager & m() const { return m_manager; } ast_manager & fm() const { return format_ns::fm(m()); } + std::string ensure_quote(symbol const& s) { + std::string str; + if (is_smt2_quoted_symbol(s)) + str = mk_smt2_quoted_symbol(s); + else + str = s.str(); + return str; + } + + symbol ensure_quote_sym(symbol const& s) { + if (is_smt2_quoted_symbol(s)) { + std::string str; + str = mk_smt2_quoted_symbol(s); + return symbol(str.c_str()); + } + else + return s; + } + void pp_var(var * v) { format * f; if (v->get_idx() < m_var_names.size()) { @@ -501,11 +520,7 @@ class smt2_printer { } format * pp_simple_attribute(char const * attr, symbol const & s) { - std::string str; - if (is_smt2_quoted_symbol(s)) - str = mk_smt2_quoted_symbol(s); - else - str = s.str(); + std::string str = ensure_quote(s); return mk_compose(m(), mk_string(m(), attr), mk_string(m(), str.c_str())); } @@ -773,7 +788,7 @@ class smt2_printer { void register_var_names(quantifier * q) { unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; i++) { - symbol name = q->get_decl_name(i); + symbol name = ensure_quote_sym(q->get_decl_name(i)); if (name.is_numerical()) { unsigned idx = 1; name = next_name("x", idx); @@ -997,6 +1012,7 @@ public: unsigned idx = 1; for (unsigned i = 0; i < num; i++) { symbol name = next_name(var_prefix, idx); + name = ensure_quote_sym(name); var_names.push_back(name); m_var_names_set.insert(name); m_var_names.push_back(name); From 83a774ac7934f40940c7f23424fced2c1a0ba590 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 4 Mar 2014 18:38:08 -0800 Subject: [PATCH 30/54] duality fix plus mbqi option --- src/duality/duality_rpfp.cpp | 21 ++++++++++++++++++++- src/duality/duality_wrapper.cpp | 3 ++- src/duality/duality_wrapper.h | 1 + src/muz/base/fixedpoint_params.pyg | 1 + src/muz/duality/duality_dl_interface.cpp | 1 + 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index e09278dd4..550f94f2d 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2847,7 +2847,12 @@ namespace Duality { } void RPFP::InterpolateByCases(Node *root, Node *node){ + timer_start("InterpolateByCases"); bool axioms_added = false; + hash_set axioms_needed; + const std::vector &theory = ls->get_axioms(); + for(unsigned i = 0; i < theory.size(); i++) + axioms_needed.insert(theory[i]); aux_solver.push(); AddEdgeToSolver(node->Outgoing); node->Annotation.SetEmpty(); @@ -2869,7 +2874,17 @@ namespace Duality { check_result foo = Check(root); if(foo != unsat) throw "should be unsat"; - AddToProofCore(*core); + + std::vector assumps, axioms_to_add; + slvr().get_proof().get_assumptions(assumps); + for(unsigned i = 0; i < assumps.size(); i++){ + (*core).insert(assumps[i]); + if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ + axioms_to_add.push_back(assumps[i]); + axioms_needed.erase(assumps[i]); + } + } + // AddToProofCore(*core); Transformer old_annot = node->Annotation; SolveSingleNode(root,node); @@ -2882,6 +2897,9 @@ namespace Duality { node->Annotation.Formula = node->Annotation.Formula.simplify(); } + for(unsigned i = 0; i < axioms_to_add.size(); i++) + aux_solver.add(axioms_to_add[i]); + if(node->Annotation.IsEmpty()){ if(!axioms_added){ // add the axioms in the off chance they are useful @@ -2906,6 +2924,7 @@ namespace Duality { delete proof_core; // shouldn't happen proof_core = core; aux_solver.pop(1); + timer_stop("InterpolateByCases"); } void RPFP::Generalize(Node *root, Node *node){ diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index cf2c803cb..345fef562 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -43,7 +43,8 @@ namespace Duality { if(models) p.set_bool("model", true); p.set_bool("unsat_core", true); - p.set_bool("mbqi",true); + bool mbqi = c.get_config().get().get_bool("mbqi",true); + p.set_bool("mbqi",mbqi); // just to test p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants if(true || extensional) diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index 2b0045023..febc3f1f1 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -182,6 +182,7 @@ namespace Duality { void set(char const * param, char const * value) { m_config.set(param,value); } void set(char const * param, bool value) { m_config.set(param,value); } void set(char const * param, int value) { m_config.set(param,value); } + config &get_config() {return m_config;} symbol str_symbol(char const * s); symbol int_symbol(int n); diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index e09e1307b..5eec77f39 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -74,6 +74,7 @@ def_module_params('fixedpoint', ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), ('profile', BOOL, False, 'DUALITY: profile run time'), + ('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 248aca163..e7c7976f6 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -141,6 +141,7 @@ lbool dl_interface::query(::expr * query) { // make a new problem and solver _d = alloc(duality_data,m_ctx.get_manager()); + _d->ctx.set("mbqi",m_ctx.get_params().mbqi()); _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); _d->rpfp = alloc(RPFP,_d->ls); From 4732e03259487da4a45391a6acc45b6adb8a4a3e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 7 Mar 2014 08:59:27 -0800 Subject: [PATCH 31/54] filter fresh constants from models Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_size_reduction_tactic.cpp | 34 ++++++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index d24f506a1..bbbc00783 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -25,6 +25,7 @@ Notes: #include"bv_decl_plugin.h" #include"expr_replacer.h" #include"extension_model_converter.h" +#include"filter_model_converter.h" #include"ast_smt2_pp.h" class bv_size_reduction_tactic : public tactic { @@ -60,6 +61,7 @@ struct bv_size_reduction_tactic::imp { obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; + ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; volatile bool m_cancel; @@ -196,6 +198,7 @@ struct bv_size_reduction_tactic::imp { numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; + app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; @@ -208,15 +211,19 @@ struct bv_size_reduction_tactic::imp { if (l.is_neg()) { unsigned i_nb = (u - l).get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); - if (i_nb < v_nb) - new_def = m_util.mk_sign_extend(v_nb - i_nb, m.mk_fresh_const(0, m_util.mk_sort(i_nb))); + if (i_nb < v_nb) { + new_const = m.mk_fresh_const(0, m_util.mk_sort(i_nb)); + new_def = m_util.mk_sign_extend(v_nb - i_nb, new_const); + } } else { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); - if (u_nb < v_nb) - new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), m.mk_fresh_const(0, m_util.mk_sort(u_nb))); + if (u_nb < v_nb) { + new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); + new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); + } } } @@ -226,6 +233,10 @@ struct bv_size_reduction_tactic::imp { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); + if (!m_fmc && new_const) + m_fmc = alloc(filter_model_converter, m); + if (new_const) + m_fmc->insert(new_const->get_decl()); } num_reduced++; } @@ -264,6 +275,7 @@ struct bv_size_reduction_tactic::imp { TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); expr * new_def = 0; + app * new_const = 0; if (l > u) { g.assert_expr(m.mk_false()); return; @@ -275,8 +287,10 @@ struct bv_size_reduction_tactic::imp { // 0 <= l <= v <= u unsigned u_nb = u.get_num_bits(); unsigned v_nb = m_util.get_bv_size(v); - if (u_nb < v_nb) - new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), m.mk_fresh_const(0, m_util.mk_sort(u_nb))); + if (u_nb < v_nb) { + new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); + new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); + } } if (new_def) { @@ -285,6 +299,10 @@ struct bv_size_reduction_tactic::imp { if (!m_mc) m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); + if (!m_fmc && new_const) + m_fmc = alloc(filter_model_converter, m); + if (new_const) + m_fmc->insert(new_const->get_decl()); } num_reduced++; TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";); @@ -309,7 +327,11 @@ struct bv_size_reduction_tactic::imp { g.update(i, new_f); } mc = m_mc.get(); + if (m_fmc) { + mc = concat(m_fmc.get(), mc.get()); + } m_mc = 0; + m_fmc = 0; } report_tactic_progress(":bv-reduced", num_reduced); TRACE("after_bv_size_reduction", g.display(tout); if (m_mc) m_mc->display(tout);); From 180f55bbdaaec6f2eee07c6c973cef2af2558add Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Tue, 11 Mar 2014 18:20:42 -0700 Subject: [PATCH 32/54] adding support for non-extensional arrays in duality --- src/duality/duality.h | 15 +- src/duality/duality_rpfp.cpp | 183 ++++++++++++++++++----- src/duality/duality_solver.cpp | 22 ++- src/duality/duality_wrapper.cpp | 17 ++- src/duality/duality_wrapper.h | 3 + src/muz/base/fixedpoint_params.pyg | 1 + src/muz/duality/duality_dl_interface.cpp | 1 + 7 files changed, 196 insertions(+), 46 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 12ba21516..137279899 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -31,6 +31,8 @@ using namespace stl_ext; namespace Duality { + class implicant_solver; + /* Generic operations on Z3 formulas */ struct Z3User { @@ -104,6 +106,9 @@ namespace Duality { FuncDecl RenumberPred(const FuncDecl &f, int n); + Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); + + protected: void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); @@ -602,6 +607,8 @@ protected: void FixCurrentState(Edge *root); void FixCurrentStateFull(Edge *edge, const expr &extra); + + void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); /** Declare a constant in the background theory. */ @@ -944,10 +951,12 @@ protected: Term UnderapproxFormula(const Term &f, hash_set &dont_cares); void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares); + hash_set &done, hash_set &dont_cares, bool extensional = true); - Term UnderapproxFullFormula(const Term &f, hash_set &dont_cares); + public: + Term UnderapproxFullFormula(const Term &f, bool extensional = true); + protected: Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); hash_map resolve_ite_memo; @@ -988,6 +997,8 @@ protected: void AddEdgeToSolver(Edge *edge); + void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); + void AddToProofCore(hash_set &core); void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 550f94f2d..2d871c324 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -410,6 +410,33 @@ namespace Duality { return res; } + Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) + { + std::pair foo(t,expr(ctx)); + std::pair::iterator, bool> bar = memo.insert(foo); + Term &res = bar.first->second; + if(!bar.second) return res; + if (t.is_app()) { + func_decl f = t.decl(); + std::vector args; + int nargs = t.num_args(); + for(int i = 0; i < nargs; i++) + args.push_back(ExtractStores(memo, t.arg(i),cnstrs,renaming)); + res = f(args.size(),&args[0]); + if(f.get_decl_kind() == Store){ + func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); + expr y = fresh(); + expr equ = ctx.make(Equal,y,res); + cnstrs.push_back(equ); + renaming[y] = res; + res = y; + } + } + else res = t; + return res; + } + + bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ if(!(lit.is_quantifier() && IsClosedFormula(lit))){ if(!lit.is_app()) @@ -1851,7 +1878,7 @@ namespace Duality { } void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares){ + hash_set &done, hash_set &dont_cares, bool extensional){ if(done.find(f) != done.end()) return; /* already processed */ if(f.is_app()){ @@ -1859,7 +1886,7 @@ namespace Duality { decl_kind k = f.decl().get_decl_kind(); if(k == Implies || k == Iff || k == And || k == Or || k == Not){ for(int i = 0; i < nargs; i++) - ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares); + ImplicantFullRed(memo,f.arg(i),lits,done,dont_cares, extensional); goto done; } } @@ -1867,6 +1894,15 @@ namespace Duality { if(dont_cares.find(f) == dont_cares.end()){ int b = SubtermTruth(memo,f); if(b != 0 && b != 1) goto done; + if(f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()){ + if(b == 1 && !extensional){ + expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); + if(!eq(x,y)) + b = 0; + } + if(b == 0) + goto done; + } expr bv = (b==1) ? f : !f; lits.push_back(bv); } @@ -1988,12 +2024,16 @@ namespace Duality { return conjoin(lits); } - RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, hash_set &dont_cares){ + RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ + hash_set dont_cares; + resolve_ite_memo.clear(); + timer_start("UnderapproxFormula"); /* first compute truth values of subterms */ hash_map memo; hash_set done; std::vector lits; - ImplicantFullRed(memo,f,lits,done,dont_cares); + ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); + timer_stop("UnderapproxFormula"); /* return conjunction of literals */ return conjoin(lits); } @@ -2538,22 +2578,6 @@ namespace Duality { ConstrainEdgeLocalized(edge,eu); } - void RPFP::FixCurrentStateFull(Edge *edge, const expr &extra){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; - for(unsigned i = 0; i < edge->constraints.size(); i++) - dual = dual && edge->constraints[i]; - // dual = dual && extra; - Term eu = UnderapproxFullFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - ConstrainEdgeLocalized(edge,eu); - } - - - - void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ if(memo[under].find(f) != memo[under].end()) return; @@ -2836,7 +2860,91 @@ namespace Duality { return ctx.make(And,lits); } - // set up edge constraint in aux solver + + /* This is a wrapper for a solver that is intended to compute + implicants from models. It works around a problem in Z3 with + models in the non-extensional array theory. It does this by + naming all of the store terms in a formula. That is, (store ...) + is replaced by "name" with an added constraint name = (store + ...). This allows us to determine from the model whether an array + equality is true or false (it is false if the two sides are + mapped to different function symbols, even if they have the same + contents). + */ + + struct implicant_solver { + RPFP *owner; + solver &aux_solver; + std::vector assumps, namings; + std::vector assump_stack, naming_stack; + hash_map renaming, renaming_memo; + + void add(const expr &e){ + expr t = e; + if(!aux_solver.extensional_array_theory()){ + unsigned i = namings.size(); + t = owner->ExtractStores(renaming_memo,t,namings,renaming); + for(; i < namings.size(); i++) + aux_solver.add(namings[i]); + } + assumps.push_back(t); + aux_solver.add(t); + } + + void push() { + assump_stack.push_back(assumps.size()); + naming_stack.push_back(namings.size()); + aux_solver.push(); + } + + // When we pop the solver, we have to re-add any namings that were lost + + void pop(int n) { + aux_solver.pop(n); + int new_assumps = assump_stack[assump_stack.size()-n]; + int new_namings = naming_stack[naming_stack.size()-n]; + for(unsigned i = new_namings; i < namings.size(); i++) + aux_solver.add(namings[i]); + assumps.resize(new_assumps); + namings.resize(new_namings); + assump_stack.resize(assump_stack.size()-1); + naming_stack.resize(naming_stack.size()-1); + } + + check_result check() { + return aux_solver.check(); + } + + model get_model() { + return aux_solver.get_model(); + } + + expr get_implicant() { + owner->dualModel = aux_solver.get_model(); + expr dual = owner->ctx.make(And,assumps); + bool ext = aux_solver.extensional_array_theory(); + expr eu = owner->UnderapproxFullFormula(dual,ext); + // if we renamed store terms, we have to undo + if(!ext) + eu = owner->SubstRec(renaming,eu); + return eu; + } + + implicant_solver(RPFP *_owner, solver &_aux_solver) + : owner(_owner), aux_solver(_aux_solver) + {} + }; + + // set up edge constraint in aux solver + void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ + if(!edge->dual.null()) + aux_solver.add(edge->dual); + for(unsigned i = 0; i < edge->constraints.size(); i++){ + expr tl = edge->constraints[i]; + aux_solver.add(tl); + } + } + void RPFP::AddEdgeToSolver(Edge *edge){ if(!edge->dual.null()) aux_solver.add(edge->dual); @@ -2853,28 +2961,29 @@ namespace Duality { const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) axioms_needed.insert(theory[i]); - aux_solver.push(); - AddEdgeToSolver(node->Outgoing); + implicant_solver is(this,aux_solver); + is.push(); + AddEdgeToSolver(is,node->Outgoing); node->Annotation.SetEmpty(); hash_set *core = new hash_set; core->insert(node->Outgoing->dual); while(1){ - aux_solver.push(); + is.push(); expr annot = !GetAnnotation(node); - aux_solver.add(annot); - if(aux_solver.check() == unsat){ - aux_solver.pop(1); + is.add(annot); + if(is.check() == unsat){ + is.pop(1); break; } - dualModel = aux_solver.get_model(); - aux_solver.pop(1); + is.pop(1); Push(); - FixCurrentStateFull(node->Outgoing,annot); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); + ConstrainEdgeLocalized(node->Outgoing,is.get_implicant()); + ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? check_result foo = Check(root); - if(foo != unsat) + if(foo != unsat){ + slvr().print("should_be_unsat.smt2"); throw "should be unsat"; - + } std::vector assumps, axioms_to_add; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++){ @@ -2890,7 +2999,7 @@ namespace Duality { { expr itp = GetAnnotation(node); - dualModel = aux_solver.get_model(); + dualModel = is.get_model(); // TODO: what does this mean? std::vector case_lits; itp = StrengthenFormulaByCaseSplitting(itp, case_lits); SetAnnotation(node,itp); @@ -2898,14 +3007,14 @@ namespace Duality { } for(unsigned i = 0; i < axioms_to_add.size(); i++) - aux_solver.add(axioms_to_add[i]); + is.add(axioms_to_add[i]); if(node->Annotation.IsEmpty()){ if(!axioms_added){ // add the axioms in the off chance they are useful const std::vector &theory = ls->get_axioms(); for(unsigned i = 0; i < theory.size(); i++) - aux_solver.add(theory[i]); + is.add(theory[i]); axioms_added = true; } else { @@ -2923,7 +3032,7 @@ namespace Duality { if(proof_core) delete proof_core; // shouldn't happen proof_core = core; - aux_solver.pop(1); + is.pop(1); timer_stop("InterpolateByCases"); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index c7eec7e9f..3c69d79c0 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -131,6 +131,7 @@ namespace Duality { Report = false; StratifiedInlining = false; RecursionBound = -1; + BatchExpand = false; { scoped_no_proof no_proofs_please(ctx.m()); #ifdef USE_RPFP_CLONE @@ -365,6 +366,7 @@ namespace Duality { bool Report; // spew on stdout bool StratifiedInlining; // Do stratified inlining as preprocessing step int RecursionBound; // Recursion bound for bounded verification + bool BatchExpand; bool SetBoolOption(bool &opt, const std::string &value){ if(value == "0") { @@ -403,6 +405,9 @@ namespace Duality { if(option == "stratified_inlining"){ return SetBoolOption(StratifiedInlining,value); } + if(option == "batch_expand"){ + return SetBoolOption(BatchExpand,value); + } if(option == "recursion_bound"){ return SetIntOption(RecursionBound,value); } @@ -2016,7 +2021,7 @@ namespace Duality { } else { was_sat = true; - tree->Push(); + tree->Push(); std::vector &expansions = stack.back().expansions; #ifndef NO_DECISIONS for(unsigned i = 0; i < expansions.size(); i++){ @@ -2027,11 +2032,16 @@ namespace Duality { if(tree->slvr().check() == unsat) throw "help!"; #endif - stack.push_back(stack_entry()); - stack.back().level = tree->slvr().get_scope_level(); - if(ExpandSomeNodes(false,1)){ - continue; + int expand_max = 1; + if(0&&duality->BatchExpand){ + int thing = stack.size() * 0.1; + expand_max = std::max(1,thing); + if(expand_max > 1) + std::cout << "foo!\n"; } + + if(ExpandSomeNodes(false,expand_max)) + continue; while(stack.size() > 1){ tree->Pop(1); stack.pop_back(); @@ -2090,6 +2100,8 @@ namespace Duality { std::list updated_nodes; virtual void ExpandNode(RPFP::Node *p){ + stack.push_back(stack_entry()); + stack.back().level = tree->slvr().get_scope_level(); stack.back().expansions.push_back(p); DerivationTree::ExpandNode(p); std::vector &new_nodes = p->Outgoing->Children; diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp index 345fef562..0f6198edd 100755 --- a/src/duality/duality_wrapper.cpp +++ b/src/duality/duality_wrapper.cpp @@ -37,7 +37,7 @@ Revision History: namespace Duality { - solver::solver(Duality::context& c, bool extensional, bool models) : object(c), the_model(c) { + solver::solver(Duality::context& c, bool _extensional, bool models) : object(c), the_model(c) { params_ref p; p.set_bool("proof", true); // this is currently useless if(models) @@ -47,7 +47,8 @@ namespace Duality { p.set_bool("mbqi",mbqi); // just to test p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants - if(true || extensional) + extensional = mbqi && (true || _extensional); + if(extensional) p.set_bool("array.extensional",true); scoped_ptr sf = mk_smt_solver_factory(); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); @@ -657,6 +658,18 @@ expr context::make_quant(decl_kind op, const std::vector &_sorts, const st pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); } + void solver::print(const char *filename) { + std::ofstream f(filename); + unsigned n = m_solver->get_num_assertions(); + if(!n) + return; + ast_smt_pp pp(m()); + for (unsigned i = 0; i < n-1; ++i) + pp.add_assumption(m_solver->get_assertion(i)); + pp.display_smt2(f, m_solver->get_assertion(n-1)); + } + + void solver::show_assertion_ids() { #if 0 unsigned n = m_solver->get_num_assertions(); diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index febc3f1f1..ccc0800b2 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -819,6 +819,7 @@ namespace Duality { model the_model; bool canceled; proof_gen_mode m_mode; + bool extensional; public: solver(context & c, bool extensional = false, bool models = true); solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} @@ -922,6 +923,7 @@ namespace Duality { unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} void show(); + void print(const char *filename); void show_assertion_ids(); proof get_proof(){ @@ -929,6 +931,7 @@ namespace Duality { return proof(ctx(),m_solver->get_proof()); } + bool extensional_array_theory() {return extensional;} }; #if 0 diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 5eec77f39..e201c2a19 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -75,6 +75,7 @@ def_module_params('fixedpoint', ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), ('profile', BOOL, False, 'DUALITY: profile run time'), ('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'), )) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index e7c7976f6..0c2a9e48a 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -205,6 +205,7 @@ lbool dl_interface::query(::expr * query) { rs->SetOption("feasible_edges",m_ctx.get_params().feasible_edges() ? "1" : "0"); 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"); unsigned rb = m_ctx.get_params().recursion_bound(); if(rb != UINT_MAX){ std::ostringstream os; os << rb; From bbab6be280c687fce57eee84bd1161356dd76853 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 14 Mar 2014 13:40:31 -0700 Subject: [PATCH 33/54] duality: eager deduction and history-based conjectures --- src/duality/duality.h | 41 ++- src/duality/duality_rpfp.cpp | 2 +- src/duality/duality_solver.cpp | 418 +++++++++++++++++++---- src/duality/duality_wrapper.h | 17 + src/muz/duality/duality_dl_interface.cpp | 70 ++-- 5 files changed, 440 insertions(+), 108 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index 137279899..b1171a698 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -204,6 +204,9 @@ protected: /** Is this a background constant? */ virtual bool is_constant(const func_decl &f) = 0; + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants() = 0; + /** Assert a background axiom. */ virtual void assert_axiom(const expr &axiom) = 0; @@ -297,6 +300,11 @@ protected: return bckg.find(f) != bckg.end(); } + /** Get the constants in the background vocabulary */ + virtual hash_set &get_constants(){ + return bckg; + } + ~iZ3LogicSolver(){ // delete ictx; delete islvr; @@ -1064,13 +1072,40 @@ protected: public: - struct Counterexample { + class Counterexample { + private: RPFP *tree; RPFP::Node *root; + public: Counterexample(){ tree = 0; root = 0; } + Counterexample(RPFP *_tree, RPFP::Node *_root){ + tree = _tree; + root = _root; + } + ~Counterexample(){ + if(tree) delete tree; + } + void swap(Counterexample &other){ + std::swap(tree,other.tree); + std::swap(root,other.root); + } + void set(RPFP *_tree, RPFP::Node *_root){ + if(tree) delete tree; + tree = _tree; + root = _root; + } + void clear(){ + if(tree) delete tree; + tree = 0; + } + RPFP *get_tree() const {return tree;} + RPFP::Node *get_root() const {return root;} + private: + Counterexample &operator=(const Counterexample &); + Counterexample(const Counterexample &); }; /** Solve the problem. You can optionally give an old @@ -1080,7 +1115,7 @@ protected: virtual bool Solve() = 0; - virtual Counterexample GetCounterexample() = 0; + virtual Counterexample &GetCounterexample() = 0; virtual bool SetOption(const std::string &option, const std::string &value) = 0; @@ -1088,7 +1123,7 @@ protected: is chiefly useful for abstraction refinement, when we want to solve a series of similar problems. */ - virtual void LearnFrom(Counterexample &old_cex) = 0; + virtual void LearnFrom(Solver *old_solver) = 0; virtual ~Solver(){} diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 2d871c324..f3deee225 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -3334,7 +3334,7 @@ namespace Duality { func_decl f = t.decl(); std::vector args; int nargs = t.num_args(); - if(nargs == 0) + if(nargs == 0 && f.get_decl_kind() == Uninterpreted) ls->declare_constant(f); // keep track of background constants for(int i = 0; i < nargs; i++) args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 3c69d79c0..d59309c00 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -54,6 +54,7 @@ Revision History: // #define KEEP_EXPANSIONS // #define USE_CACHING_RPFP // #define PROPAGATE_BEFORE_CHECK +#define NEW_STRATIFIED_INLINING #define USE_RPFP_CLONE #define USE_NEW_GEN_CANDS @@ -82,7 +83,7 @@ namespace Duality { rpfp = _rpfp; } virtual void Extend(RPFP::Node *node){} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){} + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} virtual void Bound(RPFP::Node *node){} virtual void Expand(RPFP::Edge *edge){} virtual void AddCover(RPFP::Node *covered, std::vector &covering){} @@ -94,6 +95,7 @@ namespace Duality { virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} virtual void Message(const std::string &msg){} + virtual void Depth(int){} virtual ~Reporter(){} }; @@ -124,6 +126,7 @@ namespace Duality { rpfp = _rpfp; reporter = 0; heuristic = 0; + unwinding = 0; FullExpand = false; NoConj = false; FeasibleEdges = true; @@ -152,6 +155,7 @@ namespace Duality { #ifdef USE_NEW_GEN_CANDS delete gen_cands_rpfp; #endif + if(unwinding) delete unwinding; } #ifdef USE_RPFP_CLONE @@ -250,6 +254,19 @@ namespace Duality { virtual void Done() {} }; + /** The Proposer class proposes conjectures eagerly. These can come + from any source, including predicate abstraction, templates, or + previous solver runs. The proposed conjectures are checked + with low effort when the unwinding is expanded. + */ + + class Proposer { + public: + /** Given a node in the unwinding, propose some conjectures */ + virtual std::vector &Propose(Node *node) = 0; + virtual ~Proposer(){}; + }; + class Covering; // see below @@ -279,6 +296,7 @@ namespace Duality { hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate int last_decisions; hash_set overapproxes; + std::vector proposers; #ifdef BOUNDED struct Counter { @@ -293,24 +311,22 @@ namespace Duality { virtual bool Solve(){ reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); #ifndef LOCALIZE_CONJECTURES - heuristic = !cex.tree ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); + heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); #else - heuristic = !cex.tree ? (Heuristic *)(new LocalHeuristic(rpfp)) + heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); #endif - cex.tree = 0; // heuristic now owns it + cex.clear(); // in case we didn't use it for heuristic + if(unwinding) delete unwinding; unwinding = new RPFP(rpfp->ls); unwinding->HornClauses = rpfp->HornClauses; indset = new Covering(this); last_decisions = 0; CreateEdgesByChildMap(); - CreateLeaves(); #ifndef TOP_DOWN - if(!StratifiedInlining){ - if(FeasibleEdges)NullaryCandidates(); - else InstantiateAllEdges(); - } + void CreateInitialUnwinding(); #else + CreateLeaves(); for(unsigned i = 0; i < leaves.size(); i++) if(!SatisfyUpperBound(leaves[i])) return false; @@ -322,11 +338,29 @@ namespace Duality { // print_profile(std::cout); delete indset; delete heuristic; - delete unwinding; + // delete unwinding; // keep the unwinding for future mining of predicates delete reporter; + for(unsigned i = 0; i < proposers.size(); i++) + delete proposers[i]; return res; } + void CreateInitialUnwinding(){ + if(!StratifiedInlining){ + CreateLeaves(); + if(FeasibleEdges)NullaryCandidates(); + else InstantiateAllEdges(); + } + else { +#ifdef NEW_STRATIFIED_INLINING + +#else + CreateLeaves(); +#endif + } + + } + void Cancel(){ // TODO } @@ -347,15 +381,19 @@ namespace Duality { } #endif - virtual void LearnFrom(Counterexample &old_cex){ - cex = old_cex; + virtual void LearnFrom(Solver *other_solver){ + // get the counterexample as a guide + cex.swap(other_solver->GetCounterexample()); + + // propose conjectures based on old unwinding + Duality *old_duality = dynamic_cast(other_solver); + if(old_duality) + proposers.push_back(new HistoryProposer(old_duality,this)); } - /** Return the counterexample */ - virtual Counterexample GetCounterexample(){ - Counterexample res = cex; - cex.tree = 0; // Cex now belongs to caller - return res; + /** Return a reference to the counterexample */ + virtual Counterexample &GetCounterexample(){ + return cex; } // options @@ -519,7 +557,11 @@ namespace Duality { c.Children.resize(edge->Children.size()); for(unsigned j = 0; j < c.Children.size(); j++) c.Children[j] = leaf_map[edge->Children[j]]; - Extend(c); + Node *new_node; + Extend(c,new_node); +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif } for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) indset->Add(*it); @@ -771,16 +813,14 @@ namespace Duality { } - /* For stratified inlining, we need a topological sort of the - nodes. */ - hash_map TopoSort; int TopoSortCounter; + std::vector SortedEdges; void DoTopoSortRec(Node *node){ if(TopoSort.find(node) != TopoSort.end()) return; - TopoSort[node] = TopoSortCounter++; // just to break cycles + TopoSort[node] = INT_MAX; // just to break cycles Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge if(edge){ std::vector &chs = edge->Children; @@ -788,22 +828,81 @@ namespace Duality { DoTopoSortRec(chs[i]); } TopoSort[node] = TopoSortCounter++; + SortedEdges.push_back(edge); } void DoTopoSort(){ TopoSort.clear(); + SortedEdges.clear(); TopoSortCounter = 0; for(unsigned i = 0; i < nodes.size(); i++) DoTopoSortRec(nodes[i]); } + + int StratifiedLeafCount; + +#ifdef NEW_STRATIFIED_INLINING + + /** Stratified inlining builds an initial layered unwinding before + switching to the LAWI strategy. Currently the number of layers + is one. Only nodes that are the targets of back edges are + consider to be leaves. This assumes we have already computed a + topological sort. + */ + + bool DoStratifiedInlining(){ + DoTopoSort(); + int depth = 1; // TODO: make this an option + std::vector > unfolding_levels(depth+1); + for(int level = 1; level <= depth; level++) + for(unsigned i = 0; i < SortedEdges.size(); i++){ + Edge *edge = SortedEdges[i]; + Node *parent = edge->Parent; + std::vector &chs = edge->Children; + std::vector my_chs(chs.size()); + for(unsigned j = 0; j < chs.size(); j++){ + Node *child = chs[j]; + int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; + if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ + if(ch_level == 0) + unfolding_levels[0][child] = CreateLeaf(child); + else + throw InternalError("in levelized unwinding"); + } + my_chs[j] = unfolding_levels[ch_level][child]; + } + Candidate cand; cand.edge = edge; cand.Children = my_chs; + Node *new_node; + bool ok = Extend(cand,new_node); + MarkExpanded(new_node); // we don't expand here -- just mark it done + if(!ok) return false; // got a counterexample + unfolding_levels[level][parent] = new_node; + } + return true; + } + + Node *CreateLeaf(Node *node){ + RPFP::Node *nchild = CreateNodeInstance(node); + MakeLeaf(nchild, /* do_not_expand = */ true); + nchild->Annotation.SetEmpty(); + return nchild; + } + + void MarkExpanded(Node *node){ + if(unexpanded.find(node) != unexpanded.end()){ + unexpanded.erase(node); + insts_of_node[node->map].push_back(node); + } + } + +#else + /** In stratified inlining, we build the unwinding from the bottom down, trying to satisfy the node bounds. We do this as a pre-pass, limiting the expansion. If we get a counterexample, we are done, else we continue as usual expanding the unwinding upward. */ - - int StratifiedLeafCount; bool DoStratifiedInlining(){ timer_start("StratifiedInlining"); @@ -826,6 +925,8 @@ namespace Duality { return true; } +#endif + /** Here, we do the downward expansion for stratified inlining */ hash_map LeafMap, StratifiedLeafMap; @@ -912,9 +1013,14 @@ namespace Duality { } Candidate cand = candidates.front(); candidates.pop_front(); - if(CandidateFeasible(cand)) - if(!Extend(cand)) + if(CandidateFeasible(cand)){ + Node *new_node; + if(!Extend(cand,new_node)) return false; +#ifdef EARLY_EXPAND + TryExpandNode(new_node); +#endif + } } } @@ -934,9 +1040,9 @@ namespace Duality { Node *CreateUnderapproxNode(Node *node){ - // cex.tree->ComputeUnderapprox(cex.root,0); + // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); - under_node->Annotation.IntersectWith(cex.root->Underapprox); + under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); AddThing(under_node->Annotation.Formula); Edge *e = unwinding->CreateLowerBoundEdge(under_node); under_node->Annotation.SetFull(); // allow this node to cover others @@ -972,9 +1078,8 @@ namespace Duality { ExpandNodeFromCoverFail(node); } #endif - if(_cex) *_cex = cex; - else delete cex.tree; // delete the cex if not required - cex.tree = 0; + if(_cex) (*_cex).swap(cex); // return the cex if asked + cex.clear(); // throw away the useless cex node->Bound = save; // put back original bound timer_stop("ProveConjecture"); return false; @@ -1354,16 +1459,20 @@ namespace Duality { } } - bool UpdateNodeToNode(Node *node, Node *top){ - if(!node->Annotation.SubsetEq(top->Annotation)){ - reporter->Update(node,top->Annotation); - indset->Update(node,top->Annotation); + bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ + if(!node->Annotation.SubsetEq(fact)){ + reporter->Update(node,fact,eager); + indset->Update(node,fact); updated_nodes.insert(node->map); - node->Annotation.IntersectWith(top->Annotation); + node->Annotation.IntersectWith(fact); return true; } return false; } + + bool UpdateNodeToNode(Node *node, Node *top){ + return Update(node,top->Annotation); + } /** Update the unwinding solution, using an interpolant for the derivation tree. */ @@ -1405,8 +1514,7 @@ namespace Duality { // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; last_decisions = end_decs - start_decs; if(res){ - cex.tree = dt.tree; - cex.root = dt.top; + cex.set(dt.tree,dt.top); // note tree is now owned by cex if(UseUnderapprox){ UpdateWithCounterexample(node,dt.tree,dt.top); } @@ -1418,6 +1526,64 @@ namespace Duality { delete dtp; return !res; } + + /* For a given nod in the unwinding, get conjectures from the + proposers and check them locally. Update the node with any true + conjectures. + */ + + void DoEagerDeduction(Node *node){ + for(unsigned i = 0; i < proposers.size(); i++){ + const std::vector &conjectures = proposers[i]->Propose(node); + for(unsigned j = 0; j < conjectures.size(); j++){ + const RPFP::Transformer &conjecture = conjectures[j]; + RPFP::Transformer bound(conjecture); + std::vector conj_vec; + unwinding->CollectConjuncts(bound.Formula,conj_vec); + for(unsigned k = 0; k < conj_vec.size(); k++){ + bound.Formula = conj_vec[k]; + if(CheckEdgeCaching(node->Outgoing,bound) == unsat) + Update(node,bound, /* eager = */ true); + //else + //std::cout << "conjecture failed\n"; + } + } + } + } + + + check_result CheckEdge(RPFP *checker, Edge *edge){ + Node *root = edge->Parent; + checker->Push(); + checker->AssertNode(root); + checker->AssertEdge(edge,1,true); + check_result res = checker->Check(root); + checker->Pop(1); + return res; + } + + check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ + + // use a dedicated solver for this edge + // TODO: can this mess be hidden somehow? + + RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? + Edge *edge = unwinding_edge->map; // get the edge in the original RPFP + RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); + Edge *checker_edge = checker->GetEdgeClone(edge); + + // copy the annotations and bound to the clone + Node *root = checker_edge->Parent; + root->Bound = bound; + for(unsigned j = 0; j < checker_edge->Children.size(); j++){ + Node *oc = unwinding_edge->Children[j]; + Node *nc = checker_edge->Children[j]; + nc->Annotation = oc->Annotation; + } + + return CheckEdge(checker,checker_edge); + } + /* If the counterexample derivation is partial due to use of underapproximations, complete it. */ @@ -1426,10 +1592,7 @@ namespace Duality { DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree if(!res) throw "Duality internal error in BuildFullCex"; - if(cex.tree) - delete cex.tree; - cex.tree = dt.tree; - cex.root = dt.top; + cex.set(dt.tree,dt.top); } void UpdateBackEdges(Node *node){ @@ -1452,25 +1615,23 @@ namespace Duality { } /** Extend the unwinding, keeping it solved. */ - bool Extend(Candidate &cand){ + bool Extend(Candidate &cand, Node *&node){ timer_start("Extend"); - Node *node = CreateNodeInstance(cand.edge->Parent); + node = CreateNodeInstance(cand.edge->Parent); CreateEdgeInstance(cand.edge,node,cand.Children); UpdateBackEdges(node); reporter->Extend(node); - bool res = SatisfyUpperBound(node); + DoEagerDeduction(node); // first be eager... + bool res = SatisfyUpperBound(node); // then be lazy if(res) indset->CloseDescendants(node); else { #ifdef UNDERAPPROX_NODES - ExpandUnderapproxNodes(cex.tree, cex.root); + ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); #endif if(UseUnderapprox) BuildFullCex(node); timer_stop("Extend"); return res; } -#ifdef EARLY_EXPAND - TryExpandNode(node); -#endif timer_stop("Extend"); return res; } @@ -1930,6 +2091,7 @@ namespace Duality { unsigned slvr_level = tree->slvr().get_scope_level(); if(slvr_level != stack.back().level) throw "stacks out of sync!"; + reporter->Depth(stack.size()); // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop check_result foo = tree->Check(top); @@ -2459,7 +2621,7 @@ namespace Duality { } bool ContainsCex(Node *node, Counterexample &cex){ - expr val = cex.tree->Eval(cex.root->Outgoing,node->Annotation.Formula); + expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); return eq(val,parent->ctx.bool_val(true)); } @@ -2478,15 +2640,15 @@ namespace Duality { Node *other = insts[i]; if(CouldCover(node,other)){ reporter()->Forcing(node,other); - if(cex.tree && !ContainsCex(other,cex)) + if(cex.get_tree() && !ContainsCex(other,cex)) continue; - if(cex.tree) {delete cex.tree; cex.tree = 0;} + cex.clear(); if(parent->ProveConjecture(node,other->Annotation,other,&cex)) if(CloseDescendants(node)) return true; } } - if(cex.tree) {delete cex.tree; cex.tree = 0;} + cex.clear(); return false; } #else @@ -2585,13 +2747,12 @@ namespace Duality { Counterexample old_cex; public: ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) - : Heuristic(_rpfp), old_cex(_old_cex) + : Heuristic(_rpfp) { + old_cex.swap(_old_cex); // take ownership from caller } ~ReplayHeuristic(){ - if(old_cex.tree) - delete old_cex.tree; } // Maps nodes of derivation tree into old cex @@ -2599,9 +2760,7 @@ namespace Duality { void Done() { cex_map.clear(); - if(old_cex.tree) - delete old_cex.tree; - old_cex.tree = 0; // only replay once! + old_cex.clear(); } void ShowNodeAndChildren(Node *n){ @@ -2623,7 +2782,7 @@ namespace Duality { } virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ - if(!high_priority || !old_cex.tree){ + if(!high_priority || !old_cex.get_tree()){ Heuristic::ChooseExpand(choices,best,false); return; } @@ -2632,7 +2791,7 @@ namespace Duality { for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ Node *node = (*it); if(cex_map.empty()) - cex_map[node] = old_cex.root; // match the root nodes + cex_map[node] = old_cex.get_root(); // match the root nodes if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! if(cex_map.find(parent) == cex_map.end()) @@ -2658,7 +2817,7 @@ namespace Duality { Node *old_node = cex_map[node]; if(!old_node) unmatched.insert(node); - else if(old_cex.tree->Empty(old_node)) + else if(old_cex.get_tree()->Empty(old_node)) unmatched.insert(node); else matched.insert(node); @@ -2732,7 +2891,120 @@ namespace Duality { } }; + /** This proposer class generates conjectures based on the + unwinding generated by a previous solver. The assumption is + that the provious solver was working on a different + abstraction of the same system. The trick is to adapt the + annotations in the old unwinding to the new predicates. We + start by generating a map from predicates and parameters in + the old problem to the new. + HACK: mapping is done by cheesy name comparison. + */ + + class HistoryProposer : public Proposer + { + Duality *old_solver; + Duality *new_solver; + hash_map > conjectures; + + public: + /** Construct a history solver. */ + HistoryProposer(Duality *_old_solver, Duality *_new_solver) + : old_solver(_old_solver), new_solver(_new_solver) { + + // tricky: names in the axioms may have changed -- map them + hash_set &old_constants = old_solver->unwinding->ls->get_constants(); + hash_set &new_constants = new_solver->rpfp->ls->get_constants(); + hash_map cmap; + for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) + cmap[GetKey(*it)] = *it; + hash_map bckg_map; + for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ + func_decl f = new_solver->ctx.translate(*it); // move to new context + if(cmap.find(GetKey(f)) != cmap.end()) + bckg_map[f] = cmap[GetKey(f)]; + // else + // std::cout << "constant not matched\n"; + } + + RPFP *old_unwinding = old_solver->unwinding; + hash_map > pred_match; + + // index all the predicates in the old unwinding + for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ + Node *node = old_unwinding->nodes[i]; + std::string key = GetKey(node); + pred_match[key].push_back(node); + } + + // match with predicates in the new RPFP + RPFP *rpfp = new_solver->rpfp; + for(unsigned i = 0; i < rpfp->nodes.size(); i++){ + Node *node = rpfp->nodes[i]; + std::string key = GetKey(node); + std::vector &matches = pred_match[key]; + for(unsigned j = 0; j < matches.size(); j++) + MatchNodes(node,matches[j],bckg_map); + } + } + + virtual std::vector &Propose(Node *node){ + return conjectures[node->map]; + } + + virtual ~HistoryProposer(){ + }; + + private: + void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ + if(old_node->Annotation.IsFull()) + return; // don't conjecture true! + hash_map var_match; + std::vector &new_params = new_node->Annotation.IndParams; + // Index the new parameters by their keys + for(unsigned i = 0; i < new_params.size(); i++) + var_match[GetKey(new_params[i])] = new_params[i]; + RPFP::Transformer &old = old_node->Annotation; + std::vector from_params = old.IndParams; + for(unsigned j = 0; j < from_params.size(); j++) + from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context + std::vector to_params = from_params; + for(unsigned j = 0; j < to_params.size(); j++){ + std::string key = GetKey(to_params[j]); + if(var_match.find(key) == var_match.end()){ + // std::cout << "unmatched parameter!\n"; + return; + } + to_params[j] = var_match[key]; + } + expr fmla = new_solver->ctx.translate(old.Formula); // get in new context + fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters + hash_map memo; + fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants + RPFP::Transformer new_annot = new_node->Annotation; + new_annot.Formula = fmla; + conjectures[new_node].push_back(new_annot); + } + + // We match names by removing suffixes beginning with double at sign + + std::string GetKey(Node *node){ + return GetKey(node->Name); + } + + std::string GetKey(const expr &var){ + return GetKey(var.decl()); + } + + std::string GetKey(const func_decl &f){ + std::string name = f.name().str(); + int idx = name.find("@@"); + if(idx >= 0) + name.erase(idx); + return name; + } + }; }; @@ -2740,8 +3012,9 @@ namespace Duality { std::ostream &s; public: StreamReporter(RPFP *_rpfp, std::ostream &_s) - : Reporter(_rpfp), s(_s) {event = 0;} + : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} int event; + int depth; void ev(){ s << "[" << event++ << "]" ; } @@ -2752,23 +3025,30 @@ namespace Duality { s << " " << rps[i]->number; s << std::endl; } - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){ + virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ ev(); s << "update " << node->number << " " << node->Name.name() << ": "; rpfp->Summarize(update.Formula); - std::cout << std::endl; + if(depth > 0) s << " (depth=" << depth << ")"; + if(eager) s << " (eager)"; + s << std::endl; } virtual void Bound(RPFP::Node *node){ ev(); s << "check " << node->number << std::endl; } virtual void Expand(RPFP::Edge *edge){ RPFP::Node *node = edge->Parent; - ev(); s << "expand " << node->map->number << " " << node->Name.name() << std::endl; + ev(); s << "expand " << node->map->number << " " << node->Name.name(); + if(depth > 0) s << " (depth=" << depth << ")"; + s << std::endl; + } + virtual void Depth(int d){ + depth = d; } virtual void AddCover(RPFP::Node *covered, std::vector &covering){ ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; for(unsigned i = 0; i < covering.size(); i++) - std::cout << covering[i]->number << " "; - std::cout << std::endl; + s << covering[i]->number << " "; + s << std::endl; } virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; @@ -2779,7 +3059,7 @@ namespace Duality { virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; rpfp->Summarize(t.Formula); - std::cout << std::endl; + s << std::endl; } virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h index ccc0800b2..a7497318a 100755 --- a/src/duality/duality_wrapper.h +++ b/src/duality/duality_wrapper.h @@ -244,6 +244,9 @@ namespace Duality { sort_kind get_sort_kind(const sort &s); + expr translate(const expr &e); + func_decl translate(const func_decl &); + void print_expr(std::ostream &s, const ast &e); fixedpoint mk_fixedpoint(); @@ -1374,6 +1377,20 @@ namespace Duality { return to_expr(a.raw()); } + inline expr context::translate(const expr &e) { + ::expr *f = to_expr(e.raw()); + if(&e.ctx().m() != &m()) // same ast manager -> no translation + throw "ast manager mismatch"; + return cook(f); + } + + inline func_decl context::translate(const func_decl &e) { + ::func_decl *f = to_func_decl(e.raw()); + if(&e.ctx().m() != &m()) // same ast manager -> no translation + throw "ast manager mismatch"; + return func_decl(*this,f); + } + typedef double clock_t; clock_t current_time(); inline void output_time(std::ostream &os, clock_t time){os << time;} diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp index 0c2a9e48a..587a184bc 100755 --- a/src/muz/duality/duality_dl_interface.cpp +++ b/src/muz/duality/duality_dl_interface.cpp @@ -64,20 +64,22 @@ namespace Duality { std::vector clauses; std::vector > clause_labels; hash_map map; // edges to clauses + Solver *old_rs; Solver::Counterexample cex; duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { ls = 0; rpfp = 0; status = StatusNull; + old_rs = 0; } ~duality_data(){ + if(old_rs) + dealloc(old_rs); if(rpfp) dealloc(rpfp); if(ls) dealloc(ls); - if(cex.tree) - delete cex.tree; } }; @@ -132,10 +134,12 @@ lbool dl_interface::query(::expr * query) { m_ctx.ensure_opened(); // if there is old data, get the cex and dispose (later) - Solver::Counterexample old_cex; duality_data *old_data = _d; - if(old_data) - old_cex = old_data->cex; + Solver *old_rs = 0; + if(old_data){ + old_rs = old_data->old_rs; + old_rs->GetCounterexample().swap(old_data->cex); + } scoped_proof generate_proofs_please(m_ctx.get_manager()); @@ -196,8 +200,9 @@ lbool dl_interface::query(::expr * query) { Solver *rs = Solver::Create("duality", _d->rpfp); - rs->LearnFrom(old_cex); // new solver gets hints from old cex - + if(old_rs) + rs->LearnFrom(old_rs); // new solver gets hints from old solver + // set its options IF_VERBOSE(1, rs->SetOption("report","1");); rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0"); @@ -231,15 +236,14 @@ lbool dl_interface::query(::expr * query) { // save the result and counterexample if there is one _d->status = ans ? StatusModel : StatusRefutation; - _d->cex = rs->GetCounterexample(); + _d->cex.swap(rs->GetCounterexample()); // take ownership of cex + _d->old_rs = rs; // save this for later hints if(old_data){ - old_data->cex.tree = 0; // we own it now - dealloc(old_data); + dealloc(old_data); // this deallocates the old solver if there is one } - - dealloc(rs); + // dealloc(rs); this is now owned by data // true means the RPFP problem is SAT, so the query is UNSAT return ans ? l_false : l_true; @@ -267,18 +271,16 @@ void dl_interface::reset_statistics() { static hash_set *local_func_decls; -static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexample &cex) { + static void print_proof(dl_interface *d, std::ostream& out, RPFP *tree, RPFP::Node *root) { context &ctx = d->dd()->ctx; - RPFP::Node &node = *cex.root; + RPFP::Node &node = *root; RPFP::Edge &edge = *node.Outgoing; // first, prove the children (that are actually used) for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!cex.tree->Empty(edge.Children[i])){ - Solver::Counterexample foo = cex; - foo.root = edge.Children[i]; - print_proof(d,out,foo); + if(!tree->Empty(edge.Children[i])){ + print_proof(d,out,tree,edge.Children[i]); } } @@ -287,7 +289,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << "(step s!" << node.number; out << " (" << node.Name.name(); for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - out << " " << cex.tree->Eval(&edge,edge.F.IndParams[i]); + out << " " << tree->Eval(&edge,edge.F.IndParams[i]); out << ")\n"; // print the rule number @@ -309,8 +311,8 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - out << " (= " << skolem << " " << cex.tree->Eval(&edge,skolem) << ")\n"; - expr local_skolem = cex.tree->Localize(&edge,skolem); + out << " (= " << skolem << " " << tree->Eval(&edge,skolem) << ")\n"; + expr local_skolem = tree->Localize(&edge,skolem); (*local_func_decls).insert(local_skolem.decl()); } } @@ -318,7 +320,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << " (labels"; std::vector labels; - cex.tree->GetLabels(&edge,labels); + tree->GetLabels(&edge,labels); for(unsigned j = 0; j < labels.size(); j++){ out << " " << labels[j]; } @@ -330,7 +332,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp out << " (ref "; for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!cex.tree->Empty(edge.Children[i])) + if(!tree->Empty(edge.Children[i])) out << " s!" << edge.Children[i]->number; else out << " true"; @@ -355,11 +357,11 @@ void dl_interface::display_certificate_non_const(std::ostream& out) { // negation of the query is the last clause -- prove it hash_set locals; local_func_decls = &locals; - print_proof(this,out,_d->cex); + print_proof(this,out,_d->cex.get_tree(),_d->cex.get_root()); out << ")\n"; out << "(model \n\""; ::model mod(m_ctx.get_manager()); - model orig_model = _d->cex.tree->dualModel; + model orig_model = _d->cex.get_tree()->dualModel; for(unsigned i = 0; i < orig_model.num_consts(); i++){ func_decl cnst = orig_model.get_const_decl(i); if(locals.find(cnst) == locals.end()){ @@ -430,10 +432,10 @@ model_ref dl_interface::get_model() { return md; } -static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { + static proof_ref extract_proof(dl_interface *d, RPFP *tree, RPFP::Node *root) { context &ctx = d->dd()->ctx; ast_manager &mgr = ctx.m(); - RPFP::Node &node = *cex.root; + RPFP::Node &node = *root; RPFP::Edge &edge = *node.Outgoing; RPFP::Edge *orig_edge = edge.map; @@ -455,21 +457,19 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { sort the_sort = t.get_quantifier_bound_sort(j); symbol name = t.get_quantifier_bound_name(j); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - expr val = cex.tree->Eval(&edge,skolem); + expr val = tree->Eval(&edge,skolem); expr_ref thing(ctx.uncook(val),mgr); substs[0].push_back(thing); - expr local_skolem = cex.tree->Localize(&edge,skolem); + expr local_skolem = tree->Localize(&edge,skolem); (*local_func_decls).insert(local_skolem.decl()); } } svector > pos; for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!cex.tree->Empty(edge.Children[i])){ + if(!tree->Empty(edge.Children[i])){ pos.push_back(std::pair(i+1,0)); - Solver::Counterexample foo = cex; - foo.root = edge.Children[i]; - proof_ref prem = extract_proof(d,foo); + proof_ref prem = extract_proof(d,tree,edge.Children[i]); prems.push_back(prem); substs.push_back(expr_ref_vector(mgr)); } @@ -478,7 +478,7 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) { func_decl f = node.Name; std::vector args; for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - args.push_back(cex.tree->Eval(&edge,edge.F.IndParams[i])); + args.push_back(tree->Eval(&edge,edge.F.IndParams[i])); expr conc = f(args); @@ -495,7 +495,7 @@ proof_ref dl_interface::get_proof() { if(_d->status == StatusRefutation){ hash_set locals; local_func_decls = &locals; - return extract_proof(this,_d->cex); + return extract_proof(this,_d->cex.get_tree(),_d->cex.get_root()); } else return proof_ref(m_ctx.get_manager()); From 663d110b722d584f54c11fad705027392af00ead Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 16 Mar 2014 12:09:53 -0700 Subject: [PATCH 34/54] interpolation fix --- src/interp/iz3translate.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index 7e0fa65d6..345381047 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -1590,6 +1590,27 @@ public: return res; } + /* This idiom takes ~P and Q=P, yielding ~Q. It uses a "rewrite" + (Q=false) = ~Q. We eliminate the rewrite by using symmetry, + congruence and modus ponens. */ + + if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_REWRITE && pr(prem(proof,0)) == PR_TRANSITIVITY && pr(prem(prem(proof,0),1)) == PR_IFF_FALSE){ + if(op(con) == Not && arg(con,0) == arg(conc(prem(proof,0)),0)){ + Iproof::node ante1 = translate_main(prem(prem(proof,0),0),false); + Iproof::node ante2 = translate_main(prem(prem(prem(proof,0),1),0),false); + ast ante1_con = conc(prem(prem(proof,0),0)); + ast eq0 = arg(ante1_con,0); + ast eq1 = arg(ante1_con,1); + ast symm_con = make(Iff,eq1,eq0); + Iproof::node ante1s = iproof->make_symmetry(symm_con,ante1_con,ante1); + ast cong_con = make(Iff,make(Not,eq1),make(Not,eq0)); + Iproof::node ante1sc = iproof->make_congruence(symm_con,cong_con,ante1s); + res = iproof->make_mp(cong_con,ante2,ante1sc); + return res; + } + } + + // translate all the premises std::vector args(nprems); for(unsigned i = 0; i < nprems; i++) @@ -1749,6 +1770,13 @@ public: res = args[0]; break; } + case PR_IFF_FALSE: { // turns ~p into p <-> false, noop for us + if(is_local(con)) + res = args[0]; + else + throw unsupported(); + break; + } case PR_COMMUTATIVITY: { ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); ast pf = iproof->make_reflexivity(comm_equiv); @@ -1756,6 +1784,7 @@ public: break; } default: + pfgoto(proof); assert(0 && "translate_main: unsupported proof rule"); throw unsupported(); } From 2417b75d8d2b2aa9862d75e51532a3f692f3dc7e Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Sun, 16 Mar 2014 15:37:19 -0700 Subject: [PATCH 35/54] duality: added restarts --- src/duality/duality.h | 4 ++ src/duality/duality_rpfp.cpp | 56 ++++++++++++++++++++++ src/duality/duality_solver.cpp | 87 +++++++++++++++++++++++----------- 3 files changed, 120 insertions(+), 27 deletions(-) diff --git a/src/duality/duality.h b/src/duality/duality.h index e35c97b50..e7516c1db 100755 --- a/src/duality/duality.h +++ b/src/duality/duality.h @@ -746,6 +746,10 @@ protected: struct bad_format { }; + // thrown on internal error + struct Bad { + }; + /** Pop a scope (see Push). Note, you cannot pop axioms. */ void Pop(int num_scopes); diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index f3deee225..7d3ddda7e 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -2954,6 +2954,8 @@ namespace Duality { } } + static int by_case_counter = 0; + void RPFP::InterpolateByCases(Node *root, Node *node){ timer_start("InterpolateByCases"); bool axioms_added = false; @@ -2968,6 +2970,7 @@ namespace Duality { hash_set *core = new hash_set; core->insert(node->Outgoing->dual); while(1){ + by_case_counter++; is.push(); expr annot = !GetAnnotation(node); is.add(annot); @@ -3009,6 +3012,23 @@ namespace Duality { for(unsigned i = 0; i < axioms_to_add.size(); i++) is.add(axioms_to_add[i]); +#define TEST_BAD +#ifdef TEST_BAD + { + static int bad_count = 0, num_bads = 1; + if(bad_count >= num_bads){ + bad_count = 0; + num_bads = num_bads * 2; + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); + } + bad_count++; + } +#endif + if(node->Annotation.IsEmpty()){ if(!axioms_added){ // add the axioms in the off chance they are useful @@ -3018,12 +3038,48 @@ namespace Duality { axioms_added = true; } else { +#ifdef KILL_ON_BAD_INTERPOLANT std::cout << "bad in InterpolateByCase -- core:\n"; +#if 0 std::vector assumps; slvr().get_proof().get_assumptions(assumps); for(unsigned i = 0; i < assumps.size(); i++) assumps[i].show(); +#endif + std::cout << "checking for inconsistency\n"; + std::cout << "model:\n"; + is.get_model().show(); + expr impl = is.get_implicant(); + std::vector conjuncts; + CollectConjuncts(impl,conjuncts,true); + std::cout << "impl:\n"; + for(unsigned i = 0; i < conjuncts.size(); i++) + conjuncts[i].show(); + std::cout << "annot:\n"; + annot.show(); + is.add(annot); + for(unsigned i = 0; i < conjuncts.size(); i++) + is.add(conjuncts[i]); + if(is.check() == unsat){ + std::cout << "inconsistent!\n"; + std::vector is_assumps; + is.aux_solver.get_proof().get_assumptions(is_assumps); + std::cout << "core:\n"; + for(unsigned i = 0; i < is_assumps.size(); i++) + is_assumps[i].show(); + } + else { + std::cout << "consistent!\n"; + is.aux_solver.print("should_be_inconsistent.smt2"); + } + std::cout << "by_case_counter = " << by_case_counter << "\n"; throw "ack!"; +#endif + Pop(1); + is.pop(1); + delete core; + timer_stop("InterpolateByCases"); + throw Bad(); } } Pop(1); diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index d59309c00..9c910a2e7 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -2078,7 +2078,24 @@ namespace Duality { stack.push_back(stack_entry()); } + struct DoRestart {}; + virtual bool Build(){ + while (true) { + try { + return BuildMain(); + } + catch (const DoRestart &) { + // clear the statck and try again + updated_nodes.clear(); + while(stack.size() > 1) + PopLevel(); + reporter->Message("restarted"); + } + } + } + + bool BuildMain(){ stack.back().level = tree->slvr().get_scope_level(); bool was_sat = true; @@ -2110,11 +2127,17 @@ namespace Duality { #ifdef NO_GENERALIZE node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); #else - if(expansions.size() == 1 && NodeTooComplicated(node)) - SimplifyNode(node); - else - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - Generalize(node); + try { + if(expansions.size() == 1 && NodeTooComplicated(node)) + SimplifyNode(node); + else + node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); + Generalize(node); + } + catch(const RPFP::Bad &){ + // bad interpolants can get us here + throw DoRestart(); + } #endif if(RecordUpdate(node)) update_count++; @@ -2134,28 +2157,8 @@ namespace Duality { tree->ComputeProofCore(); // need to compute the proof core before popping solver bool propagated = false; while(1) { - std::vector &expansions = stack.back().expansions; bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - // tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; - } - } - RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - RemoveExpansion(node); - } - stack.pop_back(); + PopLevel(); if(stack.size() == 1)break; if(prev_level_used){ Node *node = stack.back().expansions[0]; @@ -2213,6 +2216,30 @@ namespace Duality { } } + void PopLevel(){ + std::vector &expansions = stack.back().expansions; + tree->Pop(1); + hash_set leaves_to_remove; + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + // if(node != top) + // tree->ConstrainParent(node->Incoming[0],node); + std::vector &cs = node->Outgoing->Children; + for(unsigned i = 0; i < cs.size(); i++){ + leaves_to_remove.insert(cs[i]); + UnmapNode(cs[i]); + if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) + throw "help!"; + } + } + RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children + for(unsigned i = 0; i < expansions.size(); i++){ + Node *node = expansions[i]; + RemoveExpansion(node); + } + stack.pop_back(); + } + bool NodeTooComplicated(Node *node){ int ops = tree->CountOperators(node->Annotation.Formula); if(ops > 10) return true; @@ -2224,7 +2251,13 @@ namespace Duality { // have to destroy the old proof to get a new interpolant timer_start("SimplifyNode"); tree->PopPush(); - tree->InterpolateByCases(top,node); + try { + tree->InterpolateByCases(top,node); + } + catch(const RPFP::Bad&){ + timer_stop("SimplifyNode"); + throw RPFP::Bad(); + } timer_stop("SimplifyNode"); } From 3e91037a4dc43c26c4d6f39629bc08f15d0b93ac Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 19 Mar 2014 12:37:05 -0700 Subject: [PATCH 36/54] duality fixes --- src/duality/duality_solver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index 9c910a2e7..d22cfe80f 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -1729,6 +1729,8 @@ namespace Duality { class DerivationTree { public: + virtual ~DerivationTree(){} + DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) : slvr(rpfp->slvr()), ctx(rpfp->ctx) @@ -2207,6 +2209,7 @@ namespace Duality { if(ExpandSomeNodes(false,expand_max)) continue; + tree->Pop(1); while(stack.size() > 1){ tree->Pop(1); stack.pop_back(); From a8fb15ce2c89be53f625f495b64373623d6c7d43 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Mar 2014 18:02:05 -0700 Subject: [PATCH 37/54] patch bounds normalization bug found by dvitek Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_size_reduction_tactic.cpp | 26 +++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index bbbc00783..d3fdb6e56 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -123,21 +123,41 @@ struct bv_size_reduction_tactic::imp { negated = true; f = to_app(f)->get_arg(0); } - + if (m_util.is_bv_sle(f, lhs, rhs)) { + bv_sz = m_util.get_bv_size(lhs); if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // v <= k - if (negated) update_signed_lower(to_app(lhs), val+numeral(1)); + val = m_util.norm(val, bv_sz, true); + if (negated) { + val += numeral(1); + if (m_util.norm(val, bv_sz, true) != val) { + // bound is infeasible. + } + else { + update_signed_lower(to_app(lhs), val); + } + } else update_signed_upper(to_app(lhs), val); } else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) { TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; ); // k <= v - if (negated) update_signed_upper(to_app(rhs), val-numeral(1)); + val = m_util.norm(val, bv_sz, true); + if (negated) { + val -= numeral(1); + if (m_util.norm(val, bv_sz, true) != val) { + // bound is infeasible. + } + else { + update_signed_upper(to_app(lhs), val); + } + } else update_signed_lower(to_app(rhs), val); } } + #if 0 else if (m_util.is_bv_ule(f, lhs, rhs)) { if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) { From a9e8045071cb5a172cadf111244cbb5f8fb16f36 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Mar 2014 20:23:54 -0700 Subject: [PATCH 38/54] fix bug reported by Nuno Lopes when query gets sliced Signed-off-by: Nikolaj Bjorner --- src/muz/bmc/dl_bmc_engine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 7a88c1188..0dcbf4644 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -1465,6 +1465,10 @@ namespace datalog { if (m_rules.get_num_rules() == 0) { return l_false; } + if (m_rules.get_predicate_rules(m_query_pred).empty()) { + return l_false; + } + if (is_linear()) { if (m_ctx.get_engine() == QBMC_ENGINE) { From 3e0e9c7f3cc22efbd96351885d0da2202dee64f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Mar 2014 20:30:58 -0700 Subject: [PATCH 39/54] parse also bit-vector constants with set-info. Reported by David Cok Signed-off-by: Nikolaj Bjorner --- src/parsers/smt2/smt2parser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 546fd3819..785f578f3 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2077,6 +2077,7 @@ namespace smt2 { void parse_option_value() { switch (curr()) { + case scanner::BV_TOKEN: case scanner::INT_TOKEN: case scanner::FLOAT_TOKEN: m_curr_cmd->set_next_arg(m_ctx, m_scanner.get_number()); From 83f88917a82b4e1169d7eb07524e01ded03f5d34 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 20 Mar 2014 17:47:41 +0000 Subject: [PATCH 40/54] bugfix for python 2.6 --- scripts/mk_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index d425f6576..883492614 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -639,7 +639,7 @@ def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): - version_string = subprocess.check_output([cc, '--version']) + version_string = check_output([cc, '--version']) return str(version_string).find('clang') != -1 def is_CXX_clangpp(): From fb2caf99e6809ae450e3f9f8127cca77ada2ed63 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Fri, 21 Mar 2014 10:35:33 -0700 Subject: [PATCH 41/54] duality fix --- src/duality/duality_rpfp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp index 7d3ddda7e..564f31891 100755 --- a/src/duality/duality_rpfp.cpp +++ b/src/duality/duality_rpfp.cpp @@ -150,8 +150,10 @@ namespace Duality { } return 0; } - if(t.is_quantifier()) - return CountOperatorsRec(memo,t.body())+2; // count 2 for a quantifier + if(t.is_quantifier()){ + int nbv = t.get_quantifier_num_bound(); + return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier + } return 0; } From d1376343c7e8362fdb5b653077c1f46fbb2c544b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 22 Mar 2014 16:42:11 +0000 Subject: [PATCH 42/54] Compilation fix. gcc 4.3.2 (on debian 5) did not like the definitions of gcd and abs in class rational, so I moved them outside of the class. Signed-off-by: Christoph M. Wintersteiger --- src/util/rational.h | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/util/rational.h b/src/util/rational.h index c02a5a2c3..0d8a23e81 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -331,22 +331,13 @@ public: return target; } - friend inline rational gcd(rational const & r1, rational const & r2) { - rational result; - m().gcd(r1.m_val, r2.m_val, result.m_val); - return result; - } + friend inline rational gcd(rational const & r1, rational const & r2); // // extended Euclid: // r1*a + r2*b = gcd // - friend inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b) { - rational result; - m().gcd(r1.m_val, r2.m_val, a.m_val, b.m_val, result.m_val); - return result; - } - + friend inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b); friend inline rational lcm(rational const & r1, rational const & r2) { rational result; @@ -378,11 +369,7 @@ public: return result; } - friend inline rational abs(rational const & r) { - rational result(r); - m().abs(result.m_val); - return result; - } + friend inline rational abs(rational const & r); rational to_rational() const { return *this; } @@ -446,5 +433,24 @@ inline rational power(rational const & r, unsigned p) { return r.expt(p); } +inline rational abs(rational const & r) { + rational result(r); + rational::m().abs(result.m_val); + return result; +} + +inline rational gcd(rational const & r1, rational const & r2) { + rational result; + rational::m().gcd(r1.m_val, r2.m_val, result.m_val); + return result; +} + +inline rational gcd(rational const & r1, rational const & r2, rational & a, rational & b) { + rational result; + rational::m().gcd(r1.m_val, r2.m_val, a.m_val, b.m_val, result.m_val); + return result; +} + + #endif /* _RATIONAL_H_ */ From e3c1cdfe8c8440897c29462ed7f7b03f739803e4 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 24 Mar 2014 11:33:09 -0700 Subject: [PATCH 43/54] interpolation fix --- src/interp/iz3proof_itp.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 9352348f4..92790e5a0 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -550,6 +550,7 @@ class iz3proof_itp_impl : public iz3proof_itp { else if(g == symm) res = simplify_symm(args); else if(g == modpon) res = simplify_modpon(args); else if(g == sum) res = simplify_sum(args); + else if(g == exmid) res = simplify_exmid(args); #if 0 else if(g == cong) res = simplify_cong(args); else if(g == modpon) res = simplify_modpon(args); @@ -1097,6 +1098,17 @@ class iz3proof_itp_impl : public iz3proof_itp { } + ast simplify_exmid(const std::vector &args){ + if(is_equivrel(args[0])){ + ast Aproves = mk_true(), Bproves = mk_true(); + ast chain = destruct_cond_ineq(args[1],Aproves,Bproves); + ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); + ast interp = contra_chain(Q2,chain); + return my_and(Aproves,my_implies(Bproves,interp)); + } + throw "bad exmid"; + } + bool is_equivrel(const ast &p){ opr o = op(p); return o == Equal || o == Iff; @@ -1381,6 +1393,8 @@ class iz3proof_itp_impl : public iz3proof_itp { if(pos == top_pos && op(equality) == Iff && !is_true(arg(equality,0))) throw "bad rewrite"; #endif + if(!is_equivrel(equality)) + throw "bad rewrite"; return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); } From c9fcf7ee96707b9a91b8e9009d2f66fdd9ddafc0 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Mon, 24 Mar 2014 17:21:29 -0700 Subject: [PATCH 44/54] interpolation fix (add simplify_cong) --- src/interp/iz3proof_itp.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp index 92790e5a0..5fa3ee021 100755 --- a/src/interp/iz3proof_itp.cpp +++ b/src/interp/iz3proof_itp.cpp @@ -551,8 +551,8 @@ class iz3proof_itp_impl : public iz3proof_itp { else if(g == modpon) res = simplify_modpon(args); else if(g == sum) res = simplify_sum(args); else if(g == exmid) res = simplify_exmid(args); -#if 0 else if(g == cong) res = simplify_cong(args); +#if 0 else if(g == modpon) res = simplify_modpon(args); else if(g == leq2eq) res = simplify_leq2eq(args); else if(g == eq2leq) res = simplify_eq2leq(args); @@ -1109,6 +1109,20 @@ class iz3proof_itp_impl : public iz3proof_itp { throw "bad exmid"; } + ast simplify_cong(const std::vector &args){ + ast Aproves = mk_true(), Bproves = mk_true(); + ast chain = destruct_cond_ineq(args[0],Aproves,Bproves); + rational pos; + if(is_numeral(args[1],pos)){ + int ipos = pos.get_unsigned(); + chain = chain_pos_add(ipos,chain); + ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); + ast interp = contra_chain(Q2,chain); + return my_and(Aproves,my_implies(Bproves,interp)); + } + throw "bad cong"; + } + bool is_equivrel(const ast &p){ opr o = op(p); return o == Equal || o == Iff; From 6f9a348f6398cecd0852f2d74d4d25ebb4d670fc Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 26 Mar 2014 17:26:06 +0000 Subject: [PATCH 45/54] removed dependency of bvsls on goal_refs Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_engine.cpp | 219 +++++++++++++++++++-------------- src/tactic/sls/sls_engine.h | 58 ++++----- src/tactic/sls/sls_evaluator.h | 4 +- src/tactic/sls/sls_tracker.h | 84 ++++++------- 4 files changed, 195 insertions(+), 170 deletions(-) diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 92471aad8..9fa2cc263 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -74,15 +74,15 @@ void sls_engine::checkpoint() { cooperate("sls"); } -bool sls_engine::full_eval(goal_ref const & g, model & mdl) { +bool sls_engine::full_eval(model & mdl) { bool res = true; - unsigned sz = g->size(); + unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz && res; i++) { checkpoint(); expr_ref o(m_manager); - if (!mdl.eval(g->form(i), o, true)) + if (!mdl.eval(m_assertions[i], o, true)) exit(ERR_INTERNAL_FATAL); res = m_manager.is_true(o.get()); @@ -93,7 +93,7 @@ bool sls_engine::full_eval(goal_ref const & g, model & mdl) { return res; } -double sls_engine::top_score(goal_ref const & g) { +double sls_engine::top_score() { #if 0 double min = m_tracker.get_score(g->form(0)); unsigned sz = g->size(); @@ -108,15 +108,15 @@ double sls_engine::top_score(goal_ref const & g) { return min; #else double top_sum = 0.0; - unsigned sz = g->size(); + unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); + expr * e = m_assertions[i]; top_sum += m_tracker.get_score(e); } TRACE("sls_top", tout << "Score distribution:"; for (unsigned i = 0; i < sz; i++) - tout << " " << m_tracker.get_score(g->form(i)); + tout << " " << m_tracker.get_score(m_assertions[i]); tout << " AVG: " << top_sum / (double)sz << std::endl;); #if _CACHE_TOP_SCORE_ @@ -127,40 +127,40 @@ double sls_engine::top_score(goal_ref const & g) { #endif } -double sls_engine::rescore(goal_ref const & g) { +double sls_engine::rescore() { m_evaluator.update_all(); m_stats.m_full_evals++; - return top_score(g); + return top_score(); } -double sls_engine::serious_score(goal_ref const & g, func_decl * fd, const mpz & new_value) { +double sls_engine::serious_score(func_decl * fd, const mpz & new_value) { m_evaluator.serious_update(fd, new_value); m_stats.m_incr_evals++; #if _CACHE_TOP_SCORE_ - return (m_tracker.get_top_sum() / g->size()); + return (m_tracker.get_top_sum() / m_assertions.size()); #else - return top_score(g); + return top_score(); #endif } -double sls_engine::incremental_score(goal_ref const & g, func_decl * fd, const mpz & new_value) { +double sls_engine::incremental_score(func_decl * fd, const mpz & new_value) { m_evaluator.update(fd, new_value); m_stats.m_incr_evals++; #if _CACHE_TOP_SCORE_ - return (m_tracker.get_top_sum() / g->size()); + return (m_tracker.get_top_sum() / m_assertions.size()); #else - return top_score(g); + return top_score(); #endif } -double sls_engine::incremental_score_prune(goal_ref const & g, func_decl * fd, const mpz & new_value) { +double sls_engine::incremental_score_prune(func_decl * fd, const mpz & new_value) { #if _EARLY_PRUNE_ m_stats.m_incr_evals++; if (m_evaluator.update_prune(fd, new_value)) #if _CACHE_TOP_SCORE_ - return (m_tracker.get_top_sum() / g->size()); + return (m_tracker.get_top_sum() / m_assertions.size()); #else - return top_score(g); + return top_score(); #endif else return 0.0; @@ -170,8 +170,13 @@ double sls_engine::incremental_score_prune(goal_ref const & g, func_decl * fd, c } // checks whether the score outcome of a given move is better than the previous score -bool sls_engine::what_if(goal_ref const & g, func_decl * fd, const unsigned & fd_inx, const mpz & temp, - double & best_score, unsigned & best_const, mpz & best_value) { +bool sls_engine::what_if( + func_decl * fd, + const unsigned & fd_inx, + const mpz & temp, + double & best_score, + unsigned & best_const, + mpz & best_value) { #ifdef Z3DEBUG mpz old_value; @@ -179,9 +184,9 @@ bool sls_engine::what_if(goal_ref const & g, func_decl * fd, const unsigned & fd #endif #if _EARLY_PRUNE_ - double r = incremental_score_prune(g, fd, temp); + double r = incremental_score_prune(fd, temp); #else - double r = incremental_score(g, fd, temp); + double r = incremental_score(fd, temp); #endif #ifdef Z3DEBUG TRACE("sls_whatif", tout << "WHAT IF " << fd->get_name() << " WERE " << m_mpz_manager.to_string(temp) << @@ -202,8 +207,15 @@ bool sls_engine::what_if(goal_ref const & g, func_decl * fd, const unsigned & fd } // same as what_if, but only applied to the score of a specific atom, not the total score -bool sls_engine::what_if_local(expr * e, func_decl * fd, const unsigned & fd_inx, const mpz & temp, - double & best_score, unsigned & best_const, mpz & best_value) { +bool sls_engine::what_if_local( + expr * e, + func_decl * fd, + const unsigned & fd_inx, + const mpz & temp, + double & best_score, + unsigned & best_const, + mpz & best_value) +{ m_evaluator.update(fd, temp); double r = m_tracker.get_score(e); if (r >= best_score) { @@ -344,13 +356,19 @@ void sls_engine::mk_random_move(ptr_vector & unsat_constants) m_mpz_manager.del(new_value); } -void sls_engine::mk_random_move(goal_ref const & g) { - mk_random_move(m_tracker.get_unsat_constants(g, m_stats.m_moves)); +void sls_engine::mk_random_move() { + mk_random_move(m_tracker.get_unsat_constants(m_assertions, m_stats.m_moves)); } // will use VNS to ignore some possible moves and increase the flips per second -double sls_engine::find_best_move_vns(goal_ref const & g, ptr_vector & to_evaluate, double score, - unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move) { +double sls_engine::find_best_move_vns( + ptr_vector & to_evaluate, + double score, + unsigned & best_const, + mpz & best_value, + unsigned & new_bit, + move_type & move) +{ mpz old_value, temp; unsigned bv_sz, max_bv_sz = 0; double new_score = score; @@ -366,31 +384,31 @@ double sls_engine::find_best_move_vns(goal_ref const & g, ptr_vector if (!m_mpz_manager.is_even(old_value)) { // for odd values, try +1 mk_inc(bv_sz, old_value, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) + if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INC; } else { // for even values, try -1 mk_dec(bv_sz, old_value, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) + if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DEC; } // try inverting mk_inv(bv_sz, old_value, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) + if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INV; // try to flip lsb mk_flip(srt, old_value, 0, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) { + if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = 0; move = MV_FLIP; } } // reset to what it was before - double check = incremental_score(g, fd, old_value); + double check = incremental_score(fd, old_value); SASSERT(check == score); } @@ -412,13 +430,13 @@ double sls_engine::find_best_move_vns(goal_ref const & g, ptr_vector { mk_flip(srt, old_value, j, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) { + if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = j; move = MV_FLIP; } } // reset to what it was before - double check = incremental_score(g, fd, old_value); + double check = incremental_score(fd, old_value); SASSERT(check == score); } m_mpz_manager.del(old_value); @@ -427,8 +445,14 @@ double sls_engine::find_best_move_vns(goal_ref const & g, ptr_vector } // finds the move that increased score the most. returns best_const = -1, if no increasing move exists. -double sls_engine::find_best_move(goal_ref const & g, ptr_vector & to_evaluate, double score, - unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move) { +double sls_engine::find_best_move( + ptr_vector & to_evaluate, + double score, + unsigned & best_const, + mpz & best_value, + unsigned & new_bit, + move_type & move) +{ mpz old_value, temp; #if _USE_MUL3_ || _USE_UNARY_MINUS_ mpz temp2; @@ -451,7 +475,7 @@ double sls_engine::find_best_move(goal_ref const & g, ptr_vector & to // What would happen if we flipped bit #i ? mk_flip(srt, old_value, j, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) { + if (what_if(fd, i, temp, new_score, best_const, best_value)) { new_bit = j; move = MV_FLIP; } @@ -462,19 +486,19 @@ double sls_engine::find_best_move(goal_ref const & g, ptr_vector & to if (!m_mpz_manager.is_even(old_value)) { // for odd values, try +1 mk_inc(bv_sz, old_value, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) + if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INC; } else { // for even values, try -1 mk_dec(bv_sz, old_value, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) + if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_DEC; } #endif // try inverting mk_inv(bv_sz, old_value, temp); - if (what_if(g, fd, i, temp, new_score, best_const, best_value)) + if (what_if(fd, i, temp, new_score, best_const, best_value)) move = MV_INV; #if _USE_UNARY_MINUS_ @@ -504,7 +528,7 @@ double sls_engine::find_best_move(goal_ref const & g, ptr_vector & to } // reset to what it was before - double check = incremental_score(g, fd, old_value); + double check = incremental_score(fd, old_value); // Andreas: does not hold anymore now that we use top level score caching //SASSERT(check == score); } @@ -572,15 +596,15 @@ double sls_engine::find_best_move_local(expr * e, ptr_vector & to_eva } // first try of intensification ... does not seem to be efficient -bool sls_engine::handle_plateau(goal_ref const & g) +bool sls_engine::handle_plateau() { - unsigned sz = g->size(); + unsigned sz = m_assertions.size(); #if _BFS_ unsigned pos = m_stats.m_moves % sz; #else unsigned pos = m_tracker.get_random_uint(16) % sz; #endif - expr * e = m_tracker.get_unsat_assertion(g, sz, pos); + expr * e = m_tracker.get_unsat_assertion(sz, pos); if (!e) return 0; @@ -634,10 +658,15 @@ bool sls_engine::handle_plateau(goal_ref const & g) } // what_if version needed in the context of 2nd intensification try, combining local and global score -bool sls_engine::what_if(goal_ref const & g, expr * e, func_decl * fd, const mpz & temp, - double & best_score, mpz & best_value, unsigned i) { - - double global_score = incremental_score(g, fd, temp); +bool sls_engine::what_if( + expr * e, + func_decl * fd, + const mpz & temp, + double & best_score, + mpz & best_value, + unsigned i) +{ + double global_score = incremental_score(fd, temp); double local_score = m_tracker.get_score(e); double new_score = i * local_score / _INTENSIFICATION_TRIES_ + (_INTENSIFICATION_TRIES_ - i) * global_score / _INTENSIFICATION_TRIES_; @@ -651,7 +680,7 @@ bool sls_engine::what_if(goal_ref const & g, expr * e, func_decl * fd, const mpz } // find_best_move version needed in the context of 2nd intensification try -double sls_engine::find_best_move_local(goal_ref const & g, expr * e, func_decl * fd, mpz & best_value, unsigned i) +double sls_engine::find_best_move_local(expr * e, func_decl * fd, mpz & best_value, unsigned i) { mpz old_value, temp; double best_score = 0; @@ -662,7 +691,7 @@ double sls_engine::find_best_move_local(goal_ref const & g, expr * e, func_decl for (unsigned j = 0; j < bv_sz && best_score < 1.0; j++) { mk_flip(srt, old_value, j, temp); - what_if(g, e, fd, temp, best_score, best_value, i); + what_if(e, fd, temp, best_score, best_value, i); } m_mpz_manager.del(old_value); @@ -672,15 +701,15 @@ double sls_engine::find_best_move_local(goal_ref const & g, expr * e, func_decl } // second try to use intensification ... also not very effective -bool sls_engine::handle_plateau(goal_ref const & g, double old_score) +bool sls_engine::handle_plateau(double old_score) { - unsigned sz = g->size(); + unsigned sz = m_assertions.size(); #if _BFS_ unsigned new_const = m_stats.m_moves % sz; #else unsigned new_const = m_tracker.get_random_uint(16) % sz; #endif - expr * e = m_tracker.get_unsat_assertion(g, sz, new_const); + expr * e = m_tracker.get_unsat_assertion(m_assertions, sz, new_const); if (!e) return 0; @@ -697,12 +726,12 @@ bool sls_engine::handle_plateau(goal_ref const & g, double old_score) for (unsigned i = 1; i <= _INTENSIFICATION_TRIES_; i++) { - new_score = find_best_move_local(g, q, fd, new_value, i); + new_score = find_best_move_local(q, fd, new_value, i); m_stats.m_moves++; m_stats.m_flips++; - global_score = incremental_score(g, fd, new_value); + global_score = incremental_score(fd, new_value); local_score = m_tracker.get_score(q); SASSERT(new_score == i * local_score / _INTENSIFICATION_TRIES_ + (_INTENSIFICATION_TRIES_ - i) * global_score / _INTENSIFICATION_TRIES_); @@ -715,7 +744,7 @@ bool sls_engine::handle_plateau(goal_ref const & g, double old_score) } // main search loop -lbool sls_engine::search(goal_ref const & g) { +lbool sls_engine::search() { lbool res = l_undef; double score = 0.0, old_score = 0.0; unsigned new_const = (unsigned)-1, new_bit = 0; @@ -723,8 +752,8 @@ lbool sls_engine::search(goal_ref const & g) { move_type move; unsigned plateau_cnt = 0; - score = rescore(g); - unsigned sz = g->size(); + score = rescore(); + unsigned sz = m_assertions.size(); #if _PERC_STICKY_ expr * e = m_tracker.get_unsat_assertion(g, m_stats.m_moves); #endif @@ -750,7 +779,7 @@ lbool sls_engine::search(goal_ref const & g) { if (m_tracker.get_random_uint(16) % 100 >= _PERC_STICKY_ || m_mpz_manager.eq(m_tracker.get_value(e), m_one)) e = m_tracker.get_unsat_assertion(g, m_stats.m_moves); #else - expr * e = m_tracker.get_unsat_assertion(g, m_stats.m_moves); + expr * e = m_tracker.get_unsat_assertion(m_assertions, m_stats.m_moves); #endif if (!e) { @@ -793,7 +822,7 @@ lbool sls_engine::search(goal_ref const & g) { #if _VNS_ score = find_best_move_vns(g, to_evaluate, score, new_const, new_value, new_bit, move); #else - score = find_best_move(g, to_evaluate, score, new_const, new_value, new_bit, move); + score = find_best_move(to_evaluate, score, new_const, new_value, new_bit, move); #endif if (new_const == static_cast(-1)) { @@ -811,14 +840,14 @@ lbool sls_engine::search(goal_ref const & g) { else #endif #if _REPICK_ - m_evaluator.randomize_local(g, m_stats.m_moves); + m_evaluator.randomize_local(m_assertions, m_stats.m_moves); #else m_evaluator.randomize_local(to_evaluate); #endif #endif #if _CACHE_TOP_SCORE_ - score = m_tracker.get_top_sum() / g->size(); + score = m_tracker.get_top_sum() / m_assertions.size(); #else score = top_score(g); #endif @@ -828,7 +857,7 @@ lbool sls_engine::search(goal_ref const & g) { #if _REAL_RS_ || _REAL_PBFS_ score = serious_score(g, fd, new_value); #else - score = incremental_score(g, fd, new_value); + score = incremental_score(fd, new_value); #endif } } @@ -840,18 +869,18 @@ bailout: } // main search loop -lbool sls_engine::search_old(goal_ref const & g) { +lbool sls_engine::search_old() { lbool res = l_undef; double score = 0.0, old_score = 0.0; unsigned new_const = (unsigned)-1, new_bit = 0; mpz new_value; move_type move; - score = rescore(g); + score = rescore(); TRACE("sls", tout << "Starting search, initial score = " << std::setprecision(32) << score << std::endl; tout << "Score distribution:"; - for (unsigned i = 0; i < g->size(); i++) - tout << " " << std::setprecision(3) << m_tracker.get_score(g->form(i)); + for (unsigned i = 0; i < m_assertions.size(); i++) + tout << " " << std::setprecision(3) << m_tracker.get_score(m_assertions[i]); tout << " TOP: " << score << std::endl;); unsigned plateau_cnt = 0; @@ -897,7 +926,7 @@ lbool sls_engine::search_old(goal_ref const & g) { old_score = score; new_const = (unsigned)-1; - ptr_vector & to_evaluate = m_tracker.get_unsat_constants(g, m_stats.m_moves); + ptr_vector & to_evaluate = m_tracker.get_unsat_constants(m_assertions, m_stats.m_moves); if (!to_evaluate.size()) { res = l_true; @@ -908,22 +937,22 @@ lbool sls_engine::search_old(goal_ref const & g) { tout << to_evaluate[i]->get_name() << std::endl;); #if _VNS_ - score = find_best_move_vns(g, to_evaluate, score, new_const, new_value, new_bit, move); + score = find_best_move_vns(to_evaluate, score, new_const, new_value, new_bit, move); #else - score = find_best_move(g, to_evaluate, score, new_const, new_value, new_bit, move); + score = find_best_move(to_evaluate, score, new_const, new_value, new_bit, move); #endif if (new_const == static_cast(-1)) { TRACE("sls", tout << "Local maximum reached; unsatisfied constraints: " << std::endl; - for (unsigned i = 0; i < g->size(); i++) { - if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) - tout << mk_ismt2_pp(g->form(i), m_manager) << std::endl; + for (unsigned i = 0; i < m_assertions.size(); i++) { + if (!m_mpz_manager.is_one(m_tracker.get_value(m_assertions[i]))) + tout << mk_ismt2_pp(m_assertions[i], m_manager) << std::endl; }); TRACE("sls_max", m_tracker.show_model(tout); tout << "Scores: " << std::endl; - for (unsigned i = 0; i < g->size(); i++) - tout << mk_ismt2_pp(g->form(i), m_manager) << " ---> " << - m_tracker.get_score(g->form(i)) << std::endl;); + for (unsigned i = 0; i < m_assertions.size(); i++) + tout << mk_ismt2_pp(m_assertions[i], m_manager) << " ---> " << + m_tracker.get_score(m_assertions[i]) << std::endl;); // Andreas: If new_const == -1, shouldn't score = old_score anyway? score = old_score; } @@ -962,14 +991,14 @@ lbool sls_engine::search_old(goal_ref const & g) { } #if _REAL_RS_ || _REAL_PBFS_ - score = serious_score(g, fd, new_value); + score = serious_score(fd, new_value); #else - score = incremental_score(g, fd, new_value); + score = incremental_score(fd, new_value); #endif TRACE("sls", tout << "Score distribution:"; - for (unsigned i = 0; i < g->size(); i++) - tout << " " << std::setprecision(3) << m_tracker.get_score(g->form(i)); + for (unsigned i = 0; i < m_assertions.size(); i++) + tout << " " << std::setprecision(3) << m_tracker.get_score(m_assertions[i]); tout << " TOP: " << score << std::endl;); } @@ -978,8 +1007,8 @@ lbool sls_engine::search_old(goal_ref const & g) { // score could theoretically be imprecise. // Andreas: it seems using top level score caching can make the score unprecise also in the other direction! bool all_true = true; - for (unsigned i = 0; i < g->size() && all_true; i++) - if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) + for (unsigned i = 0; i < m_assertions.size() && all_true; i++) + if (!m_mpz_manager.is_one(m_tracker.get_value(m_assertions[i]))) all_true = false; if (all_true) { res = l_true; // sat @@ -1010,17 +1039,17 @@ lbool sls_engine::search_old(goal_ref const & g) { TRACE("sls", tout << "In a plateau (" << plateau_cnt << "/" << m_plateau_limit << "); randomizing locally." << std::endl;); #if _INTENSIFICATION_ handle_plateau(g, score); - //handle_plateau(g); + //handle_plateau(); #else - m_evaluator.randomize_local(g, m_stats.m_moves); + m_evaluator.randomize_local(m_assertions, m_stats.m_moves); #endif - //mk_random_move(g); - score = top_score(g); + //mk_random_move(); + score = top_score(); if (score >= 1.0) { bool all_true = true; - for (unsigned i = 0; i < g->size() && all_true; i++) - if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) + for (unsigned i = 0; i < m_assertions.size() && all_true; i++) + if (!m_mpz_manager.is_one(m_tracker.get_value(m_assertions[i]))) all_true = false; if (all_true) { res = l_true; // sat @@ -1046,6 +1075,10 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { m_produce_models = g->models_enabled(); + for (unsigned i = 0; i < g->size(); i++) + assert_expr(g->form(i)); + + verbose_stream() << "_BFS_ " << _BFS_ << std::endl; verbose_stream() << "_FOCUS_ " << _FOCUS_ << std::endl; verbose_stream() << "_PERC_STICKY_ " << _PERC_STICKY_ << std::endl; @@ -1089,7 +1122,7 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { #if _WEIGHT_TOGGLE_ m_tracker.set_weight_dist_factor(_WEIGHT_DIST_FACTOR_); #endif - m_tracker.initialize(g); + m_tracker.initialize(m_assertions); lbool res = l_undef; m_restart_limit = _RESTART_LIMIT_; @@ -1098,14 +1131,14 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { checkpoint(); report_tactic_progress("Searching... restarts left:", m_max_restarts - m_stats.m_restarts); - res = search(g); + res = search(); if (res == l_undef) { #if _RESTART_INIT_ - m_tracker.randomize(g); + m_tracker.randomize(); #else - m_tracker.reset(g); + m_tracker.reset(m_assertions); #endif } } while (m_stats.m_stopwatch.get_current_seconds() < _TIMELIMIT_ && res != l_true && m_stats.m_restarts++ < m_max_restarts); diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index 878354c6d..8458817b6 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -71,6 +71,7 @@ protected: bv_util m_bv_util; sls_tracker m_tracker; sls_evaluator m_evaluator; + ptr_vector m_assertions; unsigned m_restart_limit; unsigned m_max_restarts; @@ -92,11 +93,12 @@ public: void updt_params(params_ref const & _p); + void assert_expr(expr * e) { m_assertions.push_back(e); } + stats const & get_stats(void) { return m_stats; } void reset_statistics(void) { m_stats.reset(); } - bool full_eval(goal_ref const & g, model & mdl); - + bool full_eval(model & mdl); void mk_add(unsigned bv_sz, const mpz & old_value, mpz & add_value, mpz & result); void mk_mul2(unsigned bv_sz, const mpz & old_value, mpz & result); @@ -104,54 +106,44 @@ public: void mk_inc(unsigned bv_sz, const mpz & old_value, mpz & incremented); void mk_dec(unsigned bv_sz, const mpz & old_value, mpz & decremented); void mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted); - void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); + void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); - double find_best_move(goal_ref const & g, ptr_vector & to_evaluate, double score, - unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); - - double find_best_move_local(expr * e, ptr_vector & to_evaluate, - unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); - - - - bool what_if(goal_ref const & g, expr * e, func_decl * fd, const mpz & temp, - double & best_score, mpz & best_value, unsigned i); - - double find_best_move_local(goal_ref const & g, expr * e, func_decl * fd, mpz & best_value, unsigned i); - - - lbool search(goal_ref const & g); + lbool search(void); void operator()(goal_ref const & g, model_converter_ref & mc); protected: void checkpoint(); - lbool search_old(goal_ref const & g); + lbool search_old(void); double get_restart_armin(unsigned cnt_restarts); - bool what_if(goal_ref const & g, func_decl * fd, const unsigned & fd_inx, const mpz & temp, + bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value); - + bool what_if(expr * e, func_decl * fd, const mpz & temp, + double & best_score, mpz & best_value, unsigned i); bool what_if_local(expr * e, func_decl * fd, const unsigned & fd_inx, const mpz & temp, double & best_score, unsigned & best_const, mpz & best_value); - double top_score(goal_ref const & g); - double rescore(goal_ref const & g); - double serious_score(goal_ref const & g, func_decl * fd, const mpz & new_value); - double incremental_score(goal_ref const & g, func_decl * fd, const mpz & new_value); + double top_score(); + double rescore(); + double serious_score(func_decl * fd, const mpz & new_value); + double incremental_score(func_decl * fd, const mpz & new_value); #if _EARLY_PRUNE_ - double incremental_score_prune(goal_ref const & g, func_decl * fd, const mpz & new_value); + double incremental_score_prune(func_decl * fd, const mpz & new_value); #endif - - double find_best_move_vns(goal_ref const & g, ptr_vector & to_evaluate, double score, - unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); - + double find_best_move(ptr_vector & to_evaluate, double score, + unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); + double find_best_move_local(expr * e, func_decl * fd, mpz & best_value, unsigned i); + double find_best_move_local(expr * e, ptr_vector & to_evaluate, + unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); + double find_best_move_vns(ptr_vector & to_evaluate, double score, + unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move); void mk_random_move(ptr_vector & unsat_constants); - void mk_random_move(goal_ref const & g); + void mk_random_move(); - bool handle_plateau(goal_ref const & g); - bool handle_plateau(goal_ref const & g, double old_score); + bool handle_plateau(void); + bool handle_plateau(double old_score); inline unsigned check_restart(unsigned curr_value); }; diff --git a/src/tactic/sls/sls_evaluator.h b/src/tactic/sls/sls_evaluator.h index 8d2a7ba1d..0053763bb 100644 --- a/src/tactic/sls/sls_evaluator.h +++ b/src/tactic/sls/sls_evaluator.h @@ -922,8 +922,8 @@ public: randomize_local(m_tracker.get_constants(e)); } - void randomize_local(goal_ref const & g, unsigned int flip) { - randomize_local(m_tracker.get_unsat_constants(g, flip)); + void randomize_local(ptr_vector const & as, unsigned int flip) { + randomize_local(m_tracker.get_unsat_constants(as, flip)); } }; diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 6ac054246..1b451e8fd 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -20,7 +20,7 @@ Notes: #ifndef _SLS_TRACKER_H_ #define _SLS_TRACKER_H_ -#include"goal.h" +#include"bv_decl_plugin.h" #include"model.h" #include"sls_compilation_settings.h" @@ -365,12 +365,12 @@ public: } }; - void calculate_expr_distances(goal_ref const & g) { + void calculate_expr_distances(ptr_vector const & as) { // precondition: m_scores is set up. - unsigned sz = g->size(); + unsigned sz = as.size(); ptr_vector stack; for (unsigned i = 0; i < sz; i++) - stack.push_back(to_app(g->form(i))); + stack.push_back(to_app(as[i])); while (!stack.empty()) { app * cur = stack.back(); stack.pop_back(); @@ -418,12 +418,12 @@ public: quick_for_each_expr(ffd_proc, visited, e); } - void initialize(goal_ref const & g) { + void initialize(ptr_vector const & as) { init_proc proc(m_manager, *this); expr_mark visited; - unsigned sz = g->size(); + unsigned sz = as.size(); for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); + expr * e = as[i]; if (!m_top_expr.contains(e)) m_top_expr.insert(e); else @@ -438,7 +438,7 @@ public: visited.reset(); for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); + expr * e = as[i]; // Andreas: Maybe not fully correct. #if _FOCUS_ == 2 || _INTENSIFICATION_ initialize_recursive(e); @@ -450,7 +450,7 @@ public: quick_for_each_expr(ffd_proc, visited, e); } - calculate_expr_distances(g); + calculate_expr_distances(as); TRACE("sls", tout << "Initial model:" << std::endl; show_model(tout); ); @@ -465,11 +465,11 @@ public: #if _EARLY_PRUNE_ for (unsigned i = 0; i < sz; i++) - setup_occs(g->form(i)); + setup_occs(as[i]); #endif #if _UCT_ - m_touched = _UCT_INIT_ ? g->size() : 1; + m_touched = _UCT_INIT_ ? as.size() : 1; #endif } @@ -606,7 +606,7 @@ public: NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. } - void randomize(goal_ref const & g) { + void randomize(ptr_vector const & as) { TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { @@ -620,13 +620,13 @@ public: TRACE("sls", tout << "Randomized model:" << std::endl; show_model(tout); ); #if _UCT_RESET_ - m_touched = _UCT_INIT_ ? g->size() : 1; - for (unsigned i = 0; i < g->size(); i++) - m_scores.find(g->form(i)).touched = 1; + m_touched = _UCT_INIT_ ? as.size() : 1; + for (unsigned i = 0; i < as.size(); i++) + m_scores.find(as[i]).touched = 1; #endif } - void reset(goal_ref const & g) { + void reset(ptr_vector const & as) { TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { @@ -636,9 +636,9 @@ public: } #if _UCT_RESET_ - m_touched = _UCT_INIT_ ? g->size() : 1; - for (unsigned i = 0; i < g->size(); i++) - m_scores.find(g->form(i)).touched = 1; + m_touched = _UCT_INIT_ ? as.size() : 1; + for (unsigned i = 0; i < as.size(); i++) + m_scores.find(as[i]).touched = 1; #endif } @@ -1029,13 +1029,13 @@ public: return m_temp_constants; } - ptr_vector & get_unsat_constants_gsat(goal_ref const & g, unsigned sz) { + ptr_vector & get_unsat_constants_gsat(ptr_vector const & as, unsigned sz) { if (sz == 1) return get_constants(); m_temp_constants.reset(); for (unsigned i = 0; i < sz; i++) { - expr * q = g->form(i); + expr * q = as[i]; if (m_mpz_manager.eq(get_value(q), m_one)) continue; ptr_vector const & this_decls = m_constants_occ.find(q); @@ -1049,22 +1049,22 @@ public: return m_temp_constants; } - expr * get_unsat_assertion(goal_ref const & g, unsigned sz, unsigned int pos) { + expr * get_unsat_assertion(ptr_vector const & as, unsigned sz, unsigned int pos) { for (unsigned i = pos; i < sz; i++) { - expr * q = g->form(i); + expr * q = as[i]; if (m_mpz_manager.neq(get_value(q), m_one)) return q; } for (unsigned i = 0; i < pos; i++) { - expr * q = g->form(i); + expr * q = as[i]; if (m_mpz_manager.neq(get_value(q), m_one)) return q; } return 0; } - ptr_vector & get_unsat_constants_walksat(goal_ref const & g, unsigned sz, unsigned int pos) { - expr * q = get_unsat_assertion(g, sz, pos); + ptr_vector & get_unsat_constants_walksat(ptr_vector const & as, unsigned sz, unsigned int pos) { + expr * q = get_unsat_assertion(as, sz, pos); // Andreas: I should probably fix this. If this is the case then the formula is SAT anyway but this is not checked in the first iteration. if (!q) return m_temp_constants; @@ -1141,19 +1141,19 @@ public: return m_temp_constants; } - ptr_vector & get_unsat_constants_crsat(goal_ref const & g, unsigned sz, unsigned int pos) { - expr * q = get_unsat_assertion(g, sz, pos); + ptr_vector & get_unsat_constants_crsat(ptr_vector const & as, unsigned sz, unsigned int pos) { + expr * q = get_unsat_assertion(as, sz, pos); if (!q) return m_temp_constants; return go_deeper(q); } - ptr_vector & get_unsat_constants(goal_ref const & g, unsigned int flip) { - unsigned sz = g->size(); + ptr_vector & get_unsat_constants(ptr_vector const & as, unsigned int flip) { + unsigned sz = as.size(); if (sz == 1) { - if (m_mpz_manager.eq(get_value(g->form(0)), m_one)) + if (m_mpz_manager.eq(get_value(as[0]), m_one)) return m_temp_constants; else return get_constants(); @@ -1202,7 +1202,7 @@ public: #else double max = -1.0; for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); + expr * e = as[i]; // for (unsigned i = 0; i < m_where_false.size(); i++) { // expr * e = m_list_false[i]; vscore = m_scores.find(e); @@ -1220,12 +1220,12 @@ public: return m_temp_constants; #if _UCT_ == 1 || _UCT_ == 3 - m_scores.find(g->form(pos)).touched++; + m_scores.find(as[pos]).touched++; m_touched++; #elif _UCT_ == 2 - m_scores.find(g->form(pos)).touched = flip; + m_scores.find(as[pos]).touched = flip; #endif - expr * e = g->form(pos); + expr * e = as[pos]; // expr * e = m_list_false[pos]; #elif _BFS_ == 3 @@ -1304,11 +1304,11 @@ public: } - expr * get_unsat_assertion(goal_ref const & g, unsigned int flip) { - unsigned sz = g->size(); + expr * get_unsat_assertion(ptr_vector const & as, unsigned int flip) { + unsigned sz = as.size(); if (sz == 1) - return g->form(0); + return as[0]; m_temp_constants.reset(); #if _FOCUS_ == 1 @@ -1352,7 +1352,7 @@ public: #else double max = -1.0; for (unsigned i = 0; i < sz; i++) { - expr * e = g->form(i); + expr * e = as[i]; // for (unsigned i = 0; i < m_where_false.size(); i++) { // expr * e = m_list_false[i]; vscore = m_scores.find(e); @@ -1370,13 +1370,13 @@ public: return 0; #if _UCT_ == 1 || _UCT_ == 3 - m_scores.find(g->form(pos)).touched++; + m_scores.find(as[pos]).touched++; m_touched++; #elif _UCT_ == 2 m_scores.find(g->form(pos)).touched = flip; #endif // return m_list_false[pos]; - return g->form(pos); + return as[pos]; #elif _BFS_ == 3 unsigned int pos = -1; @@ -1430,7 +1430,7 @@ public: unsigned int pos = get_random_uint(16) % sz; return get_unsat_assertion(g, sz, pos); #endif - return g->form(pos); + return as[pos]; #elif _FOCUS_ == 2 #if _BFS_ unsigned int pos = flip % sz; From fcada914d525e97703ce8001e6054bb972eeb017 Mon Sep 17 00:00:00 2001 From: Ken McMillan Date: Wed, 26 Mar 2014 14:10:21 -0700 Subject: [PATCH 46/54] duality fix --- src/duality/duality_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index d22cfe80f..7e36495b1 100755 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -324,7 +324,7 @@ namespace Duality { last_decisions = 0; CreateEdgesByChildMap(); #ifndef TOP_DOWN - void CreateInitialUnwinding(); + CreateInitialUnwinding(); #else CreateLeaves(); for(unsigned i = 0; i < leaves.size(); i++) From be2066a1a63158d2087fa9ee33d537c28dd52af5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 27 Mar 2014 13:34:21 +0000 Subject: [PATCH 47/54] disabled old code --- src/tactic/sls/sls_engine.cpp | 4 +++- src/tactic/sls/sls_engine.h | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 9fa2cc263..cc06e4f78 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -868,6 +868,7 @@ bailout: return res; } +#if 0 // Old code. // main search loop lbool sls_engine::search_old() { lbool res = l_undef; @@ -1060,6 +1061,7 @@ lbool sls_engine::search_old() { } } } +#endif bailout: m_mpz_manager.del(new_value); @@ -1190,4 +1192,4 @@ unsigned sls_engine::check_restart(unsigned curr_value) return 0; } return 1; -} \ No newline at end of file +} diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index 8458817b6..5d4c9a0fc 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -114,7 +114,6 @@ public: protected: void checkpoint(); - lbool search_old(void); double get_restart_armin(unsigned cnt_restarts); bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, @@ -148,4 +147,4 @@ protected: inline unsigned check_restart(unsigned curr_value); }; -#endif \ No newline at end of file +#endif From c5e059211f5e1c919ab21714520d4f2434a50c07 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 27 Mar 2014 13:37:04 +0000 Subject: [PATCH 48/54] bugfix --- src/tactic/sls/sls_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index cc06e4f78..10aaf6601 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -1061,13 +1061,13 @@ lbool sls_engine::search_old() { } } } -#endif bailout: m_mpz_manager.del(new_value); return res; } +#endif void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { if (g->inconsistent()) { From 883762d54a7352997bb496a0d75e29d48df851f7 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Mar 2014 12:27:06 +0000 Subject: [PATCH 49/54] removed dependency of bvsls on goal_refs Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_tracker.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 1b451e8fd..e29cebe90 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -276,13 +276,13 @@ public: } #endif - void uct_forget(goal_ref const & g) { + void uct_forget(ptr_vector & as) { expr * e; unsigned touched_old, touched_new; - for (unsigned i = 0; i < g->size(); i++) + for (unsigned i = 0; i < as.size(); i++) { - e = g->form(i); + e = as[i]; touched_old = m_scores.find(e).touched; touched_new = (unsigned)((touched_old - 1) * _UCT_FORGET_FACTOR_ + 1); m_scores.find(e).touched = touched_new; From 176715aea07ed98b4fedf188cf2e1af1a3e4f624 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Mar 2014 12:28:40 +0000 Subject: [PATCH 50/54] compilation fix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_tracker.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index e29cebe90..f5c77fc35 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -20,6 +20,7 @@ Notes: #ifndef _SLS_TRACKER_H_ #define _SLS_TRACKER_H_ +#include"for_each_expr.h" #include"bv_decl_plugin.h" #include"model.h" From 8e5659ac4cf795ba2f375515cbb2a5c0874ec0c0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Mar 2014 12:30:15 +0000 Subject: [PATCH 51/54] compilation fixes Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_engine.h | 1 + src/tactic/sls/sls_tracker.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index 5d4c9a0fc..96041d09e 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -22,6 +22,7 @@ Notes: #include"stopwatch.h" #include"lbool.h" #include"model_converter.h" +#include"goal.h" #include"sls_compilation_settings.h" #include"sls_tracker.h" diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index f5c77fc35..50d0e4188 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -21,6 +21,7 @@ Notes: #define _SLS_TRACKER_H_ #include"for_each_expr.h" +#include"ast_smt2_pp.h" #include"bv_decl_plugin.h" #include"model.h" From 24d662ba49a487517758e6e2e7489ffdf47075fb Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Mar 2014 14:58:59 +0000 Subject: [PATCH 52/54] bvsls refactoring Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_engine.cpp | 21 ++++++++++++--------- src/tactic/sls/sls_engine.h | 2 ++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 10aaf6601..e7ee2abbd 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -1069,6 +1069,16 @@ bailout: } #endif +void sls_engine::init_tracker() { +#if _WEIGHT_DIST_ == 4 + m_tracker.set_weight_dist_factor(m_stats.m_stopwatch.get_current_seconds() / _TIMELIMIT_); +#endif +#if _WEIGHT_TOGGLE_ + m_tracker.set_weight_dist_factor(_WEIGHT_DIST_FACTOR_); +#endif + m_tracker.initialize(m_assertions); +} + void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { if (g->inconsistent()) { mc = 0; @@ -1078,8 +1088,7 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { m_produce_models = g->models_enabled(); for (unsigned i = 0; i < g->size(); i++) - assert_expr(g->form(i)); - + assert_expr(g->form(i)); verbose_stream() << "_BFS_ " << _BFS_ << std::endl; verbose_stream() << "_FOCUS_ " << _FOCUS_ << std::endl; @@ -1118,13 +1127,7 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { verbose_stream() << "_EARLY_PRUNE_ " << _EARLY_PRUNE_ << std::endl; verbose_stream() << "_CACHE_TOP_SCORE_ " << _CACHE_TOP_SCORE_ << std::endl; -#if _WEIGHT_DIST_ == 4 - m_tracker.set_weight_dist_factor(m_stats.m_stopwatch.get_current_seconds() / _TIMELIMIT_); -#endif -#if _WEIGHT_TOGGLE_ - m_tracker.set_weight_dist_factor(_WEIGHT_DIST_FACTOR_); -#endif - m_tracker.initialize(m_assertions); + init_tracker(); lbool res = l_undef; m_restart_limit = _RESTART_LIMIT_; diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index 96041d09e..c56d6ec40 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -109,6 +109,8 @@ public: void mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted); void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); + void init_tracker(void); + lbool search(void); void operator()(goal_ref const & g, model_converter_ref & mc); From 0f5d2e010dd207b99c20696bef8eb0945d569be5 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Mar 2014 15:26:52 +0000 Subject: [PATCH 53/54] bvsls refactoring Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_engine.cpp | 44 +++++++++++++++++++---------------- src/tactic/sls/sls_engine.h | 1 + 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index e7ee2abbd..2b359608a 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -1125,8 +1125,31 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { verbose_stream() << "_TYPE_RSTEP_ " << _TYPE_RSTEP_ << std::endl; verbose_stream() << "_PERM_RSTEP_ " << _PERM_RSTEP_ << std::endl; verbose_stream() << "_EARLY_PRUNE_ " << _EARLY_PRUNE_ << std::endl; - verbose_stream() << "_CACHE_TOP_SCORE_ " << _CACHE_TOP_SCORE_ << std::endl; + verbose_stream() << "_CACHE_TOP_SCORE_ " << _CACHE_TOP_SCORE_ << std::endl; + lbool res = operator()(); + + if (res == l_true) { + report_tactic_progress("Number of flips:", m_stats.m_moves); + for (unsigned i = 0; i < g->size(); i++) + if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) + { + verbose_stream() << "Terminated before all assertions were SAT!" << std::endl; + NOT_IMPLEMENTED_YET(); + } + + if (m_produce_models) { + model_ref mdl = m_tracker.get_model(); + mc = model2model_converter(mdl.get()); + TRACE("sls_model", mc->display(tout);); + } + g->reset(); + } + else + mc = 0; +} + +lbool sls_engine::operator()() { init_tracker(); lbool res = l_undef; @@ -1149,25 +1172,6 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { } while (m_stats.m_stopwatch.get_current_seconds() < _TIMELIMIT_ && res != l_true && m_stats.m_restarts++ < m_max_restarts); verbose_stream() << "(restarts: " << m_stats.m_restarts << " flips: " << m_stats.m_moves << " time: " << std::fixed << std::setprecision(2) << m_stats.m_stopwatch.get_current_seconds() << " fps: " << (m_stats.m_moves / m_stats.m_stopwatch.get_current_seconds()) << ")" << std::endl; - - if (res == l_true) { - report_tactic_progress("Number of flips:", m_stats.m_moves); - for (unsigned i = 0; i < g->size(); i++) - if (!m_mpz_manager.is_one(m_tracker.get_value(g->form(i)))) - { - verbose_stream() << "Terminated before all assertions were SAT!" << std::endl; - NOT_IMPLEMENTED_YET(); - } - - if (m_produce_models) { - model_ref mdl = m_tracker.get_model(); - mc = model2model_converter(mdl.get()); - TRACE("sls_model", mc->display(tout);); - } - g->reset(); - } - else - mc = 0; } unsigned sls_engine::check_restart(unsigned curr_value) diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index c56d6ec40..c6e3af155 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -113,6 +113,7 @@ public: lbool search(void); + lbool operator()(); void operator()(goal_ref const & g, model_converter_ref & mc); protected: From f8ee58b30122b53e5cd2fb9c346788c2c732f0f0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 28 Mar 2014 15:28:02 +0000 Subject: [PATCH 54/54] bvsls bugfix Signed-off-by: Christoph M. Wintersteiger --- src/tactic/sls/sls_engine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 2b359608a..dc4451bb5 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -1172,6 +1172,8 @@ lbool sls_engine::operator()() { } while (m_stats.m_stopwatch.get_current_seconds() < _TIMELIMIT_ && res != l_true && m_stats.m_restarts++ < m_max_restarts); verbose_stream() << "(restarts: " << m_stats.m_restarts << " flips: " << m_stats.m_moves << " time: " << std::fixed << std::setprecision(2) << m_stats.m_stopwatch.get_current_seconds() << " fps: " << (m_stats.m_moves / m_stats.m_stopwatch.get_current_seconds()) << ")" << std::endl; + + return res; } unsigned sls_engine::check_restart(unsigned curr_value)