3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-19 12:23:38 +00:00

merged changes from linux

This commit is contained in:
Ken McMillan 2014-03-14 14:51:39 -07:00
commit 675820ff67
8 changed files with 728 additions and 175 deletions

View file

@ -29,6 +29,8 @@ using namespace stl_ext;
namespace Duality { namespace Duality {
class implicant_solver;
/* Generic operations on Z3 formulas */ /* Generic operations on Z3 formulas */
struct Z3User { struct Z3User {
@ -80,6 +82,8 @@ namespace Duality {
Term SubstAtom(hash_map<ast, Term> &memo, const expr &t, const expr &atom, const expr &val); Term SubstAtom(hash_map<ast, Term> &memo, const expr &t, const expr &atom, const expr &val);
Term CloneQuantAndSimp(const expr &t, const expr &body);
Term RemoveRedundancy(const Term &t); Term RemoveRedundancy(const Term &t);
Term IneqToEq(const Term &t); Term IneqToEq(const Term &t);
@ -100,6 +104,9 @@ namespace Duality {
FuncDecl RenumberPred(const FuncDecl &f, int n); FuncDecl RenumberPred(const FuncDecl &f, int n);
Term ExtractStores(hash_map<ast, Term> &memo, const Term &t, std::vector<expr> &cnstrs, hash_map<ast,expr> &renaming);
protected: protected:
void SummarizeRec(hash_set<ast> &memo, std::vector<expr> &lits, int &ops, const Term &t); void SummarizeRec(hash_set<ast> &memo, std::vector<expr> &lits, int &ops, const Term &t);
@ -195,6 +202,9 @@ protected:
/** Is this a background constant? */ /** Is this a background constant? */
virtual bool is_constant(const func_decl &f) = 0; virtual bool is_constant(const func_decl &f) = 0;
/** Get the constants in the background vocabulary */
virtual hash_set<func_decl> &get_constants() = 0;
/** Assert a background axiom. */ /** Assert a background axiom. */
virtual void assert_axiom(const expr &axiom) = 0; virtual void assert_axiom(const expr &axiom) = 0;
@ -288,6 +298,11 @@ protected:
return bckg.find(f) != bckg.end(); return bckg.find(f) != bckg.end();
} }
/** Get the constants in the background vocabulary */
virtual hash_set<func_decl> &get_constants(){
return bckg;
}
~iZ3LogicSolver(){ ~iZ3LogicSolver(){
// delete ictx; // delete ictx;
delete islvr; delete islvr;
@ -598,6 +613,8 @@ protected:
void FixCurrentState(Edge *root); void FixCurrentState(Edge *root);
void FixCurrentStateFull(Edge *edge, const expr &extra); void FixCurrentStateFull(Edge *edge, const expr &extra);
void FixCurrentStateFull(Edge *edge, const std::vector<expr> &assumps, const hash_map<ast,expr> &renaming);
/** Declare a constant in the background theory. */ /** Declare a constant in the background theory. */
@ -940,10 +957,12 @@ protected:
Term UnderapproxFormula(const Term &f, hash_set<ast> &dont_cares); Term UnderapproxFormula(const Term &f, hash_set<ast> &dont_cares);
void ImplicantFullRed(hash_map<ast,int> &memo, const Term &f, std::vector<Term> &lits, void ImplicantFullRed(hash_map<ast,int> &memo, const Term &f, std::vector<Term> &lits,
hash_set<ast> &done, hash_set<ast> &dont_cares); hash_set<ast> &done, hash_set<ast> &dont_cares, bool extensional = true);
Term UnderapproxFullFormula(const Term &f, hash_set<ast> &dont_cares); public:
Term UnderapproxFullFormula(const Term &f, bool extensional = true);
protected:
Term ToRuleRec(Edge *e, hash_map<ast,Term> &memo, const Term &t, std::vector<expr> &quants); Term ToRuleRec(Edge *e, hash_map<ast,Term> &memo, const Term &t, std::vector<expr> &quants);
hash_map<ast,Term> resolve_ite_memo; hash_map<ast,Term> resolve_ite_memo;
@ -984,6 +1003,8 @@ protected:
void AddEdgeToSolver(Edge *edge); void AddEdgeToSolver(Edge *edge);
void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge);
void AddToProofCore(hash_set<ast> &core); void AddToProofCore(hash_set<ast> &core);
void GetGroundLitsUnderQuants(hash_set<ast> *memo, const Term &f, std::vector<Term> &res, int under); void GetGroundLitsUnderQuants(hash_set<ast> *memo, const Term &f, std::vector<Term> &res, int under);
@ -1049,13 +1070,40 @@ protected:
public: public:
struct Counterexample { class Counterexample {
private:
RPFP *tree; RPFP *tree;
RPFP::Node *root; RPFP::Node *root;
public:
Counterexample(){ Counterexample(){
tree = 0; tree = 0;
root = 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 /** Solve the problem. You can optionally give an old
@ -1065,7 +1113,7 @@ protected:
virtual bool Solve() = 0; virtual bool Solve() = 0;
virtual Counterexample GetCounterexample() = 0; virtual Counterexample &GetCounterexample() = 0;
virtual bool SetOption(const std::string &option, const std::string &value) = 0; virtual bool SetOption(const std::string &option, const std::string &value) = 0;
@ -1073,7 +1121,7 @@ protected:
is chiefly useful for abstraction refinement, when we want to is chiefly useful for abstraction refinement, when we want to
solve a series of similar problems. */ solve a series of similar problems. */
virtual void LearnFrom(Counterexample &old_cex) = 0; virtual void LearnFrom(Solver *old_solver) = 0;
virtual ~Solver(){} virtual ~Solver(){}

View file

@ -410,6 +410,33 @@ namespace Duality {
return res; return res;
} }
Z3User::Term Z3User::ExtractStores(hash_map<ast, Term> &memo, const Term &t, std::vector<expr> &cnstrs, hash_map<ast,expr> &renaming)
{
std::pair<ast,Term> foo(t,expr(ctx));
std::pair<hash_map<ast,Term>::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<Term> 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){ bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){
if(!(lit.is_quantifier() && IsClosedFormula(lit))){ if(!(lit.is_quantifier() && IsClosedFormula(lit))){
if(!lit.is_app()) if(!lit.is_app())
@ -523,6 +550,25 @@ namespace Duality {
return foo; 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<expr> 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<expr> 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<ast, Term> &memo, const expr &t, const expr &atom, const expr &val){ Z3User::Term Z3User::SubstAtom(hash_map<ast, Term> &memo, const expr &t, const expr &atom, const expr &val){
std::pair<ast,Term> foo(t,expr(ctx)); std::pair<ast,Term> foo(t,expr(ctx));
std::pair<hash_map<ast,Term>::iterator, bool> bar = memo.insert(foo); std::pair<hash_map<ast,Term>::iterator, bool> bar = memo.insert(foo);
@ -1832,7 +1878,7 @@ namespace Duality {
} }
void RPFP::ImplicantFullRed(hash_map<ast,int> &memo, const Term &f, std::vector<Term> &lits, void RPFP::ImplicantFullRed(hash_map<ast,int> &memo, const Term &f, std::vector<Term> &lits,
hash_set<ast> &done, hash_set<ast> &dont_cares){ hash_set<ast> &done, hash_set<ast> &dont_cares, bool extensional){
if(done.find(f) != done.end()) if(done.find(f) != done.end())
return; /* already processed */ return; /* already processed */
if(f.is_app()){ if(f.is_app()){
@ -1840,7 +1886,7 @@ namespace Duality {
decl_kind k = f.decl().get_decl_kind(); decl_kind k = f.decl().get_decl_kind();
if(k == Implies || k == Iff || k == And || k == Or || k == Not){ if(k == Implies || k == Iff || k == And || k == Or || k == Not){
for(int i = 0; i < nargs; i++) 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; goto done;
} }
} }
@ -1848,6 +1894,15 @@ namespace Duality {
if(dont_cares.find(f) == dont_cares.end()){ if(dont_cares.find(f) == dont_cares.end()){
int b = SubtermTruth(memo,f); int b = SubtermTruth(memo,f);
if(b != 0 && b != 1) goto done; 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; expr bv = (b==1) ? f : !f;
lits.push_back(bv); lits.push_back(bv);
} }
@ -1969,12 +2024,16 @@ namespace Duality {
return conjoin(lits); return conjoin(lits);
} }
RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, hash_set<ast> &dont_cares){ RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){
hash_set<ast> dont_cares;
resolve_ite_memo.clear();
timer_start("UnderapproxFormula");
/* first compute truth values of subterms */ /* first compute truth values of subterms */
hash_map<ast,int> memo; hash_map<ast,int> memo;
hash_set<ast> done; hash_set<ast> done;
std::vector<Term> lits; std::vector<Term> lits;
ImplicantFullRed(memo,f,lits,done,dont_cares); ImplicantFullRed(memo,f,lits,done,dont_cares, extensional);
timer_stop("UnderapproxFormula");
/* return conjunction of literals */ /* return conjunction of literals */
return conjoin(lits); return conjoin(lits);
} }
@ -2519,22 +2578,6 @@ namespace Duality {
ConstrainEdgeLocalized(edge,eu); ConstrainEdgeLocalized(edge,eu);
} }
void RPFP::FixCurrentStateFull(Edge *edge, const expr &extra){
hash_set<ast> 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<ast> *memo, const Term &f, std::vector<Term> &res, int under){ void RPFP::GetGroundLitsUnderQuants(hash_set<ast> *memo, const Term &f, std::vector<Term> &res, int under){
if(memo[under].find(f) != memo[under].end()) if(memo[under].find(f) != memo[under].end())
return; return;
@ -2817,7 +2860,91 @@ namespace Duality {
return ctx.make(And,lits); 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<expr> assumps, namings;
std::vector<int> assump_stack, naming_stack;
hash_map<ast,expr> 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){ void RPFP::AddEdgeToSolver(Edge *edge){
if(!edge->dual.null()) if(!edge->dual.null())
aux_solver.add(edge->dual); aux_solver.add(edge->dual);
@ -2828,47 +2955,66 @@ namespace Duality {
} }
void RPFP::InterpolateByCases(Node *root, Node *node){ void RPFP::InterpolateByCases(Node *root, Node *node){
timer_start("InterpolateByCases");
bool axioms_added = false; bool axioms_added = false;
aux_solver.push(); hash_set<ast> axioms_needed;
AddEdgeToSolver(node->Outgoing); const std::vector<expr> &theory = ls->get_axioms();
for(unsigned i = 0; i < theory.size(); i++)
axioms_needed.insert(theory[i]);
implicant_solver is(this,aux_solver);
is.push();
AddEdgeToSolver(is,node->Outgoing);
node->Annotation.SetEmpty(); node->Annotation.SetEmpty();
hash_set<ast> *core = new hash_set<ast>; hash_set<ast> *core = new hash_set<ast>;
core->insert(node->Outgoing->dual); core->insert(node->Outgoing->dual);
while(1){ while(1){
aux_solver.push(); is.push();
expr annot = !GetAnnotation(node); expr annot = !GetAnnotation(node);
aux_solver.add(annot); is.add(annot);
if(aux_solver.check() == unsat){ if(is.check() == unsat){
aux_solver.pop(1); is.pop(1);
break; break;
} }
dualModel = aux_solver.get_model(); is.pop(1);
aux_solver.pop(1);
Push(); Push();
FixCurrentStateFull(node->Outgoing,annot); ConstrainEdgeLocalized(node->Outgoing,is.get_implicant());
ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this?
check_result foo = Check(root); check_result foo = Check(root);
if(foo != unsat) if(foo != unsat){
slvr().print("should_be_unsat.smt2");
throw "should be unsat"; throw "should be unsat";
AddToProofCore(*core); }
std::vector<expr> 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; Transformer old_annot = node->Annotation;
SolveSingleNode(root,node); SolveSingleNode(root,node);
{ {
expr itp = GetAnnotation(node); expr itp = GetAnnotation(node);
dualModel = aux_solver.get_model(); dualModel = is.get_model(); // TODO: what does this mean?
std::vector<expr> case_lits; std::vector<expr> case_lits;
itp = StrengthenFormulaByCaseSplitting(itp, case_lits); itp = StrengthenFormulaByCaseSplitting(itp, case_lits);
SetAnnotation(node,itp); SetAnnotation(node,itp);
node->Annotation.Formula = node->Annotation.Formula.simplify(); node->Annotation.Formula = node->Annotation.Formula.simplify();
} }
for(unsigned i = 0; i < axioms_to_add.size(); i++)
is.add(axioms_to_add[i]);
if(node->Annotation.IsEmpty()){ if(node->Annotation.IsEmpty()){
if(!axioms_added){ if(!axioms_added){
// add the axioms in the off chance they are useful // add the axioms in the off chance they are useful
const std::vector<expr> &theory = ls->get_axioms(); const std::vector<expr> &theory = ls->get_axioms();
for(unsigned i = 0; i < theory.size(); i++) for(unsigned i = 0; i < theory.size(); i++)
aux_solver.add(theory[i]); is.add(theory[i]);
axioms_added = true; axioms_added = true;
} }
else { else {
@ -2886,7 +3032,8 @@ namespace Duality {
if(proof_core) if(proof_core)
delete proof_core; // shouldn't happen delete proof_core; // shouldn't happen
proof_core = core; proof_core = core;
aux_solver.pop(1); is.pop(1);
timer_stop("InterpolateByCases");
} }
void RPFP::Generalize(Node *root, Node *node){ void RPFP::Generalize(Node *root, Node *node){
@ -3187,7 +3334,7 @@ namespace Duality {
func_decl f = t.decl(); func_decl f = t.decl();
std::vector<Term> args; std::vector<Term> args;
int nargs = t.num_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 ls->declare_constant(f); // keep track of background constants
for(int i = 0; i < nargs; i++) for(int i = 0; i < nargs; i++)
args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); args.push_back(SubstBoundRec(memo, subst, level, t.arg(i)));

View file

@ -54,6 +54,7 @@ Revision History:
// #define KEEP_EXPANSIONS // #define KEEP_EXPANSIONS
// #define USE_CACHING_RPFP // #define USE_CACHING_RPFP
// #define PROPAGATE_BEFORE_CHECK // #define PROPAGATE_BEFORE_CHECK
#define NEW_STRATIFIED_INLINING
#define USE_RPFP_CLONE #define USE_RPFP_CLONE
#define USE_NEW_GEN_CANDS #define USE_NEW_GEN_CANDS
@ -82,7 +83,7 @@ namespace Duality {
rpfp = _rpfp; rpfp = _rpfp;
} }
virtual void Extend(RPFP::Node *node){} 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 Bound(RPFP::Node *node){}
virtual void Expand(RPFP::Edge *edge){} virtual void Expand(RPFP::Edge *edge){}
virtual void AddCover(RPFP::Node *covered, std::vector<RPFP::Node *> &covering){} virtual void AddCover(RPFP::Node *covered, std::vector<RPFP::Node *> &covering){}
@ -94,6 +95,7 @@ namespace Duality {
virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){}
virtual void Reject(RPFP::Edge *edge, const std::vector<RPFP::Node *> &Children){} virtual void Reject(RPFP::Edge *edge, const std::vector<RPFP::Node *> &Children){}
virtual void Message(const std::string &msg){} virtual void Message(const std::string &msg){}
virtual void Depth(int){}
virtual ~Reporter(){} virtual ~Reporter(){}
}; };
@ -124,6 +126,7 @@ namespace Duality {
rpfp = _rpfp; rpfp = _rpfp;
reporter = 0; reporter = 0;
heuristic = 0; heuristic = 0;
unwinding = 0;
FullExpand = false; FullExpand = false;
NoConj = false; NoConj = false;
FeasibleEdges = true; FeasibleEdges = true;
@ -131,6 +134,7 @@ namespace Duality {
Report = false; Report = false;
StratifiedInlining = false; StratifiedInlining = false;
RecursionBound = -1; RecursionBound = -1;
BatchExpand = false;
{ {
scoped_no_proof no_proofs_please(ctx.m()); scoped_no_proof no_proofs_please(ctx.m());
#ifdef USE_RPFP_CLONE #ifdef USE_RPFP_CLONE
@ -151,6 +155,7 @@ namespace Duality {
#ifdef USE_NEW_GEN_CANDS #ifdef USE_NEW_GEN_CANDS
delete gen_cands_rpfp; delete gen_cands_rpfp;
#endif #endif
if(unwinding) delete unwinding;
} }
#ifdef USE_RPFP_CLONE #ifdef USE_RPFP_CLONE
@ -249,6 +254,19 @@ namespace Duality {
virtual void Done() {} 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<RPFP::Transformer> &Propose(Node *node) = 0;
virtual ~Proposer(){};
};
class Covering; // see below class Covering; // see below
@ -278,6 +296,7 @@ namespace Duality {
hash_map<Node *, Node *> underapprox_map; // maps underapprox nodes to the nodes they approximate hash_map<Node *, Node *> underapprox_map; // maps underapprox nodes to the nodes they approximate
int last_decisions; int last_decisions;
hash_set<Node *> overapproxes; hash_set<Node *> overapproxes;
std::vector<Proposer *> proposers;
#ifdef BOUNDED #ifdef BOUNDED
struct Counter { struct Counter {
@ -292,24 +311,22 @@ namespace Duality {
virtual bool Solve(){ virtual bool Solve(){
reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp);
#ifndef LOCALIZE_CONJECTURES #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 #else
heuristic = !cex.tree ? (Heuristic *)(new LocalHeuristic(rpfp)) heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp))
: (Heuristic *)(new ReplayHeuristic(rpfp,cex)); : (Heuristic *)(new ReplayHeuristic(rpfp,cex));
#endif #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 = new RPFP(rpfp->ls);
unwinding->HornClauses = rpfp->HornClauses; unwinding->HornClauses = rpfp->HornClauses;
indset = new Covering(this); indset = new Covering(this);
last_decisions = 0; last_decisions = 0;
CreateEdgesByChildMap(); CreateEdgesByChildMap();
CreateLeaves();
#ifndef TOP_DOWN #ifndef TOP_DOWN
if(!StratifiedInlining){ void CreateInitialUnwinding();
if(FeasibleEdges)NullaryCandidates();
else InstantiateAllEdges();
}
#else #else
CreateLeaves();
for(unsigned i = 0; i < leaves.size(); i++) for(unsigned i = 0; i < leaves.size(); i++)
if(!SatisfyUpperBound(leaves[i])) if(!SatisfyUpperBound(leaves[i]))
return false; return false;
@ -321,11 +338,29 @@ namespace Duality {
// print_profile(std::cout); // print_profile(std::cout);
delete indset; delete indset;
delete heuristic; delete heuristic;
delete unwinding; // delete unwinding; // keep the unwinding for future mining of predicates
delete reporter; delete reporter;
for(unsigned i = 0; i < proposers.size(); i++)
delete proposers[i];
return res; return res;
} }
void CreateInitialUnwinding(){
if(!StratifiedInlining){
CreateLeaves();
if(FeasibleEdges)NullaryCandidates();
else InstantiateAllEdges();
}
else {
#ifdef NEW_STRATIFIED_INLINING
#else
CreateLeaves();
#endif
}
}
void Cancel(){ void Cancel(){
// TODO // TODO
} }
@ -346,15 +381,19 @@ namespace Duality {
} }
#endif #endif
virtual void LearnFrom(Counterexample &old_cex){ virtual void LearnFrom(Solver *other_solver){
cex = old_cex; // get the counterexample as a guide
cex.swap(other_solver->GetCounterexample());
// propose conjectures based on old unwinding
Duality *old_duality = dynamic_cast<Duality *>(other_solver);
if(old_duality)
proposers.push_back(new HistoryProposer(old_duality,this));
} }
/** Return the counterexample */ /** Return a reference to the counterexample */
virtual Counterexample GetCounterexample(){ virtual Counterexample &GetCounterexample(){
Counterexample res = cex; return cex;
cex.tree = 0; // Cex now belongs to caller
return res;
} }
// options // options
@ -365,6 +404,7 @@ namespace Duality {
bool Report; // spew on stdout bool Report; // spew on stdout
bool StratifiedInlining; // Do stratified inlining as preprocessing step bool StratifiedInlining; // Do stratified inlining as preprocessing step
int RecursionBound; // Recursion bound for bounded verification int RecursionBound; // Recursion bound for bounded verification
bool BatchExpand;
bool SetBoolOption(bool &opt, const std::string &value){ bool SetBoolOption(bool &opt, const std::string &value){
if(value == "0") { if(value == "0") {
@ -403,6 +443,9 @@ namespace Duality {
if(option == "stratified_inlining"){ if(option == "stratified_inlining"){
return SetBoolOption(StratifiedInlining,value); return SetBoolOption(StratifiedInlining,value);
} }
if(option == "batch_expand"){
return SetBoolOption(BatchExpand,value);
}
if(option == "recursion_bound"){ if(option == "recursion_bound"){
return SetIntOption(RecursionBound,value); return SetIntOption(RecursionBound,value);
} }
@ -514,7 +557,11 @@ namespace Duality {
c.Children.resize(edge->Children.size()); c.Children.resize(edge->Children.size());
for(unsigned j = 0; j < c.Children.size(); j++) for(unsigned j = 0; j < c.Children.size(); j++)
c.Children[j] = leaf_map[edge->Children[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) for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it)
indset->Add(*it); indset->Add(*it);
@ -766,16 +813,14 @@ namespace Duality {
} }
/* For stratified inlining, we need a topological sort of the
nodes. */
hash_map<Node *, int> TopoSort; hash_map<Node *, int> TopoSort;
int TopoSortCounter; int TopoSortCounter;
std::vector<Edge *> SortedEdges;
void DoTopoSortRec(Node *node){ void DoTopoSortRec(Node *node){
if(TopoSort.find(node) != TopoSort.end()) if(TopoSort.find(node) != TopoSort.end())
return; 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 Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge
if(edge){ if(edge){
std::vector<Node *> &chs = edge->Children; std::vector<Node *> &chs = edge->Children;
@ -783,22 +828,81 @@ namespace Duality {
DoTopoSortRec(chs[i]); DoTopoSortRec(chs[i]);
} }
TopoSort[node] = TopoSortCounter++; TopoSort[node] = TopoSortCounter++;
SortedEdges.push_back(edge);
} }
void DoTopoSort(){ void DoTopoSort(){
TopoSort.clear(); TopoSort.clear();
SortedEdges.clear();
TopoSortCounter = 0; TopoSortCounter = 0;
for(unsigned i = 0; i < nodes.size(); i++) for(unsigned i = 0; i < nodes.size(); i++)
DoTopoSortRec(nodes[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<hash_map<Node *,Node *> > 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<Node *> &chs = edge->Children;
std::vector<Node *> 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 /** In stratified inlining, we build the unwinding from the bottom
down, trying to satisfy the node bounds. We do this as a pre-pass, 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, limiting the expansion. If we get a counterexample, we are done,
else we continue as usual expanding the unwinding upward. else we continue as usual expanding the unwinding upward.
*/ */
int StratifiedLeafCount;
bool DoStratifiedInlining(){ bool DoStratifiedInlining(){
timer_start("StratifiedInlining"); timer_start("StratifiedInlining");
@ -821,6 +925,8 @@ namespace Duality {
return true; return true;
} }
#endif
/** Here, we do the downward expansion for stratified inlining */ /** Here, we do the downward expansion for stratified inlining */
hash_map<Node *, Node *> LeafMap, StratifiedLeafMap; hash_map<Node *, Node *> LeafMap, StratifiedLeafMap;
@ -907,9 +1013,14 @@ namespace Duality {
} }
Candidate cand = candidates.front(); Candidate cand = candidates.front();
candidates.pop_front(); candidates.pop_front();
if(CandidateFeasible(cand)) if(CandidateFeasible(cand)){
if(!Extend(cand)) Node *new_node;
if(!Extend(cand,new_node))
return false; return false;
#ifdef EARLY_EXPAND
TryExpandNode(new_node);
#endif
}
} }
} }
@ -929,9 +1040,9 @@ namespace Duality {
Node *CreateUnderapproxNode(Node *node){ 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-- */); 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); AddThing(under_node->Annotation.Formula);
Edge *e = unwinding->CreateLowerBoundEdge(under_node); Edge *e = unwinding->CreateLowerBoundEdge(under_node);
under_node->Annotation.SetFull(); // allow this node to cover others under_node->Annotation.SetFull(); // allow this node to cover others
@ -967,9 +1078,8 @@ namespace Duality {
ExpandNodeFromCoverFail(node); ExpandNodeFromCoverFail(node);
} }
#endif #endif
if(_cex) *_cex = cex; if(_cex) (*_cex).swap(cex); // return the cex if asked
else delete cex.tree; // delete the cex if not required cex.clear(); // throw away the useless cex
cex.tree = 0;
node->Bound = save; // put back original bound node->Bound = save; // put back original bound
timer_stop("ProveConjecture"); timer_stop("ProveConjecture");
return false; return false;
@ -1349,16 +1459,20 @@ namespace Duality {
} }
} }
bool UpdateNodeToNode(Node *node, Node *top){ bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){
if(!node->Annotation.SubsetEq(top->Annotation)){ if(!node->Annotation.SubsetEq(fact)){
reporter->Update(node,top->Annotation); reporter->Update(node,fact,eager);
indset->Update(node,top->Annotation); indset->Update(node,fact);
updated_nodes.insert(node->map); updated_nodes.insert(node->map);
node->Annotation.IntersectWith(top->Annotation); node->Annotation.IntersectWith(fact);
return true; return true;
} }
return false; return false;
} }
bool UpdateNodeToNode(Node *node, Node *top){
return Update(node,top->Annotation);
}
/** Update the unwinding solution, using an interpolant for the /** Update the unwinding solution, using an interpolant for the
derivation tree. */ derivation tree. */
@ -1400,8 +1514,7 @@ namespace Duality {
// std::cout << "decisions: " << (end_decs - start_decs) << std::endl; // std::cout << "decisions: " << (end_decs - start_decs) << std::endl;
last_decisions = end_decs - start_decs; last_decisions = end_decs - start_decs;
if(res){ if(res){
cex.tree = dt.tree; cex.set(dt.tree,dt.top); // note tree is now owned by cex
cex.root = dt.top;
if(UseUnderapprox){ if(UseUnderapprox){
UpdateWithCounterexample(node,dt.tree,dt.top); UpdateWithCounterexample(node,dt.tree,dt.top);
} }
@ -1413,6 +1526,64 @@ namespace Duality {
delete dtp; delete dtp;
return !res; 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<RPFP::Transformer> &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<expr> 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 /* If the counterexample derivation is partial due to
use of underapproximations, complete it. */ use of underapproximations, complete it. */
@ -1421,10 +1592,7 @@ namespace Duality {
DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand);
bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree
if(!res) throw "Duality internal error in BuildFullCex"; if(!res) throw "Duality internal error in BuildFullCex";
if(cex.tree) cex.set(dt.tree,dt.top);
delete cex.tree;
cex.tree = dt.tree;
cex.root = dt.top;
} }
void UpdateBackEdges(Node *node){ void UpdateBackEdges(Node *node){
@ -1447,25 +1615,23 @@ namespace Duality {
} }
/** Extend the unwinding, keeping it solved. */ /** Extend the unwinding, keeping it solved. */
bool Extend(Candidate &cand){ bool Extend(Candidate &cand, Node *&node){
timer_start("Extend"); timer_start("Extend");
Node *node = CreateNodeInstance(cand.edge->Parent); node = CreateNodeInstance(cand.edge->Parent);
CreateEdgeInstance(cand.edge,node,cand.Children); CreateEdgeInstance(cand.edge,node,cand.Children);
UpdateBackEdges(node); UpdateBackEdges(node);
reporter->Extend(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); if(res) indset->CloseDescendants(node);
else { else {
#ifdef UNDERAPPROX_NODES #ifdef UNDERAPPROX_NODES
ExpandUnderapproxNodes(cex.tree, cex.root); ExpandUnderapproxNodes(cex.get_tree(), cex.get_root());
#endif #endif
if(UseUnderapprox) BuildFullCex(node); if(UseUnderapprox) BuildFullCex(node);
timer_stop("Extend"); timer_stop("Extend");
return res; return res;
} }
#ifdef EARLY_EXPAND
TryExpandNode(node);
#endif
timer_stop("Extend"); timer_stop("Extend");
return res; return res;
} }
@ -1916,6 +2082,7 @@ namespace Duality {
stack.back().level = tree->slvr().get_scope_level(); stack.back().level = tree->slvr().get_scope_level();
bool was_sat = true; bool was_sat = true;
int update_failures = 0;
while (true) while (true)
{ {
@ -1924,6 +2091,7 @@ namespace Duality {
unsigned slvr_level = tree->slvr().get_scope_level(); unsigned slvr_level = tree->slvr().get_scope_level();
if(slvr_level != stack.back().level) if(slvr_level != stack.back().level)
throw "stacks out of sync!"; throw "stacks out of sync!";
reporter->Depth(stack.size());
// res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop
check_result foo = tree->Check(top); check_result foo = tree->Check(top);
@ -1954,10 +2122,14 @@ namespace Duality {
heuristic->Update(node->map); // make it less likely to expand this node in future heuristic->Update(node->map); // make it less likely to expand this node in future
} }
if(update_count == 0){ if(update_count == 0){
if(was_sat) if(was_sat){
throw "Help!"; update_failures++;
if(update_failures > 10)
throw Incompleteness();
}
reporter->Message("backtracked without learning"); reporter->Message("backtracked without learning");
} }
else update_failures = 0;
} }
tree->ComputeProofCore(); // need to compute the proof core before popping solver tree->ComputeProofCore(); // need to compute the proof core before popping solver
bool propagated = false; bool propagated = false;
@ -2011,7 +2183,7 @@ namespace Duality {
} }
else { else {
was_sat = true; was_sat = true;
tree->Push(); tree->Push();
std::vector<Node *> &expansions = stack.back().expansions; std::vector<Node *> &expansions = stack.back().expansions;
#ifndef NO_DECISIONS #ifndef NO_DECISIONS
for(unsigned i = 0; i < expansions.size(); i++){ for(unsigned i = 0; i < expansions.size(); i++){
@ -2022,11 +2194,16 @@ namespace Duality {
if(tree->slvr().check() == unsat) if(tree->slvr().check() == unsat)
throw "help!"; throw "help!";
#endif #endif
stack.push_back(stack_entry()); int expand_max = 1;
stack.back().level = tree->slvr().get_scope_level(); if(0&&duality->BatchExpand){
if(ExpandSomeNodes(false,1)){ int thing = stack.size() * 0.1;
continue; expand_max = std::max(1,thing);
if(expand_max > 1)
std::cout << "foo!\n";
} }
if(ExpandSomeNodes(false,expand_max))
continue;
while(stack.size() > 1){ while(stack.size() > 1){
tree->Pop(1); tree->Pop(1);
stack.pop_back(); stack.pop_back();
@ -2085,6 +2262,8 @@ namespace Duality {
std::list<Node *> updated_nodes; std::list<Node *> updated_nodes;
virtual void ExpandNode(RPFP::Node *p){ 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); stack.back().expansions.push_back(p);
DerivationTree::ExpandNode(p); DerivationTree::ExpandNode(p);
std::vector<Node *> &new_nodes = p->Outgoing->Children; std::vector<Node *> &new_nodes = p->Outgoing->Children;
@ -2442,7 +2621,7 @@ namespace Duality {
} }
bool ContainsCex(Node *node, Counterexample &cex){ 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)); return eq(val,parent->ctx.bool_val(true));
} }
@ -2461,15 +2640,15 @@ namespace Duality {
Node *other = insts[i]; Node *other = insts[i];
if(CouldCover(node,other)){ if(CouldCover(node,other)){
reporter()->Forcing(node,other); reporter()->Forcing(node,other);
if(cex.tree && !ContainsCex(other,cex)) if(cex.get_tree() && !ContainsCex(other,cex))
continue; continue;
if(cex.tree) {delete cex.tree; cex.tree = 0;} cex.clear();
if(parent->ProveConjecture(node,other->Annotation,other,&cex)) if(parent->ProveConjecture(node,other->Annotation,other,&cex))
if(CloseDescendants(node)) if(CloseDescendants(node))
return true; return true;
} }
} }
if(cex.tree) {delete cex.tree; cex.tree = 0;} cex.clear();
return false; return false;
} }
#else #else
@ -2568,13 +2747,12 @@ namespace Duality {
Counterexample old_cex; Counterexample old_cex;
public: public:
ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex)
: Heuristic(_rpfp), old_cex(_old_cex) : Heuristic(_rpfp)
{ {
old_cex.swap(_old_cex); // take ownership from caller
} }
~ReplayHeuristic(){ ~ReplayHeuristic(){
if(old_cex.tree)
delete old_cex.tree;
} }
// Maps nodes of derivation tree into old cex // Maps nodes of derivation tree into old cex
@ -2582,9 +2760,7 @@ namespace Duality {
void Done() { void Done() {
cex_map.clear(); cex_map.clear();
if(old_cex.tree) old_cex.clear();
delete old_cex.tree;
old_cex.tree = 0; // only replay once!
} }
void ShowNodeAndChildren(Node *n){ void ShowNodeAndChildren(Node *n){
@ -2606,7 +2782,7 @@ namespace Duality {
} }
virtual void ChooseExpand(const std::set<RPFP::Node *> &choices, std::set<RPFP::Node *> &best, bool high_priority, bool best_only){ virtual void ChooseExpand(const std::set<RPFP::Node *> &choices, std::set<RPFP::Node *> &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); Heuristic::ChooseExpand(choices,best,false);
return; return;
} }
@ -2615,7 +2791,7 @@ namespace Duality {
for(std::set<Node *>::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ for(std::set<Node *>::iterator it = choices.begin(), en = choices.end(); it != en; ++it){
Node *node = (*it); Node *node = (*it);
if(cex_map.empty()) 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 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! Node *parent = node->Incoming[0]->Parent; // assumes we are a tree!
if(cex_map.find(parent) == cex_map.end()) if(cex_map.find(parent) == cex_map.end())
@ -2641,7 +2817,7 @@ namespace Duality {
Node *old_node = cex_map[node]; Node *old_node = cex_map[node];
if(!old_node) if(!old_node)
unmatched.insert(node); unmatched.insert(node);
else if(old_cex.tree->Empty(old_node)) else if(old_cex.get_tree()->Empty(old_node))
unmatched.insert(node); unmatched.insert(node);
else else
matched.insert(node); matched.insert(node);
@ -2715,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<Node *, std::vector<RPFP::Transformer> > 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<func_decl> &old_constants = old_solver->unwinding->ls->get_constants();
hash_set<func_decl> &new_constants = new_solver->rpfp->ls->get_constants();
hash_map<std::string,func_decl> cmap;
for(hash_set<func_decl>::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it)
cmap[GetKey(*it)] = *it;
hash_map<func_decl,func_decl> bckg_map;
for(hash_set<func_decl>::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<std::string, std::vector<Node *> > 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<Node *> &matches = pred_match[key];
for(unsigned j = 0; j < matches.size(); j++)
MatchNodes(node,matches[j],bckg_map);
}
}
virtual std::vector<RPFP::Transformer> &Propose(Node *node){
return conjectures[node->map];
}
virtual ~HistoryProposer(){
};
private:
void MatchNodes(Node *new_node, Node *old_node, hash_map<func_decl,func_decl> &bckg_map){
if(old_node->Annotation.IsFull())
return; // don't conjecture true!
hash_map<std::string, expr> var_match;
std::vector<expr> &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<expr> 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<expr> 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<ast,expr> 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;
}
};
}; };
@ -2723,8 +3012,9 @@ namespace Duality {
std::ostream &s; std::ostream &s;
public: public:
StreamReporter(RPFP *_rpfp, std::ostream &_s) StreamReporter(RPFP *_rpfp, std::ostream &_s)
: Reporter(_rpfp), s(_s) {event = 0;} : Reporter(_rpfp), s(_s) {event = 0; depth = -1;}
int event; int event;
int depth;
void ev(){ void ev(){
s << "[" << event++ << "]" ; s << "[" << event++ << "]" ;
} }
@ -2735,23 +3025,30 @@ namespace Duality {
s << " " << rps[i]->number; s << " " << rps[i]->number;
s << std::endl; 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() << ": "; ev(); s << "update " << node->number << " " << node->Name.name() << ": ";
rpfp->Summarize(update.Formula); 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){ virtual void Bound(RPFP::Node *node){
ev(); s << "check " << node->number << std::endl; ev(); s << "check " << node->number << std::endl;
} }
virtual void Expand(RPFP::Edge *edge){ virtual void Expand(RPFP::Edge *edge){
RPFP::Node *node = edge->Parent; 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<RPFP::Node *> &covering){ virtual void AddCover(RPFP::Node *covered, std::vector<RPFP::Node *> &covering){
ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by ";
for(unsigned i = 0; i < covering.size(); i++) for(unsigned i = 0; i < covering.size(); i++)
std::cout << covering[i]->number << " "; s << covering[i]->number << " ";
std::cout << std::endl; s << std::endl;
} }
virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){
ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl;
@ -2762,7 +3059,7 @@ namespace Duality {
virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){
ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": ";
rpfp->Summarize(t.Formula); rpfp->Summarize(t.Formula);
std::cout << std::endl; s << std::endl;
} }
virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ virtual void Dominates(RPFP::Node *node, RPFP::Node *other){
ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl;

View file

@ -37,16 +37,18 @@ Revision History:
namespace Duality { 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; params_ref p;
p.set_bool("proof", true); // this is currently useless p.set_bool("proof", true); // this is currently useless
if(models) if(models)
p.set_bool("model", true); p.set_bool("model", true);
p.set_bool("unsat_core", 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_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants
p.set_uint("mbqi.max_iterations",1); // 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); p.set_bool("array.extensional",true);
scoped_ptr<solver_factory> sf = mk_smt_solver_factory(); scoped_ptr<solver_factory> sf = mk_smt_solver_factory();
m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); m_solver = (*sf)(m(), p, true, true, true, ::symbol::null);
@ -656,6 +658,18 @@ expr context::make_quant(decl_kind op, const std::vector<sort> &_sorts, const st
pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); 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() { void solver::show_assertion_ids() {
#if 0 #if 0
unsigned n = m_solver->get_num_assertions(); unsigned n = m_solver->get_num_assertions();

View file

@ -182,6 +182,7 @@ namespace Duality {
void set(char const * param, char const * value) { m_config.set(param,value); } 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, bool value) { m_config.set(param,value); }
void set(char const * param, int 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 str_symbol(char const * s);
symbol int_symbol(int n); symbol int_symbol(int n);
@ -243,6 +244,9 @@ namespace Duality {
sort_kind get_sort_kind(const sort &s); 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); void print_expr(std::ostream &s, const ast &e);
fixedpoint mk_fixedpoint(); fixedpoint mk_fixedpoint();
@ -818,6 +822,7 @@ namespace Duality {
model the_model; model the_model;
bool canceled; bool canceled;
proof_gen_mode m_mode; proof_gen_mode m_mode;
bool extensional;
public: public:
solver(context & c, bool extensional = false, bool models = true); solver(context & c, bool extensional = false, bool models = true);
solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;}
@ -921,6 +926,7 @@ namespace Duality {
unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();}
void show(); void show();
void print(const char *filename);
void show_assertion_ids(); void show_assertion_ids();
proof get_proof(){ proof get_proof(){
@ -928,6 +934,7 @@ namespace Duality {
return proof(ctx(),m_solver->get_proof()); return proof(ctx(),m_solver->get_proof());
} }
bool extensional_array_theory() {return extensional;}
}; };
#if 0 #if 0
@ -1370,6 +1377,20 @@ namespace Duality {
return to_expr(a.raw()); 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; typedef double clock_t;
clock_t current_time(); clock_t current_time();
inline void output_time(std::ostream &os, clock_t time){os << time;} inline void output_time(std::ostream &os, clock_t time){os << time;}

View file

@ -367,11 +367,17 @@ class iz3proof_itp_impl : public iz3proof_itp {
} }
default: default:
{ {
symb s = sym(itp2); opr o = op(itp2);
if(s == sforall || s == sexists) if(o == Uninterpreted){
res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); symb s = sym(itp2);
else 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; res = itp2;
}
} }
} }
} }
@ -403,11 +409,17 @@ class iz3proof_itp_impl : public iz3proof_itp {
} }
default: default:
{ {
symb s = sym(itp1); opr o = op(itp1);
if(s == sforall || s == sexists) if(o == Uninterpreted){
res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); symb s = sym(itp1);
else 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; res = itp1;
}
} }
} }
} }
@ -462,18 +474,20 @@ class iz3proof_itp_impl : public iz3proof_itp {
std::pair<hash_map<ast,ast>::iterator,bool> bar = subst_memo.insert(foo); std::pair<hash_map<ast,ast>::iterator,bool> bar = subst_memo.insert(foo);
ast &res = bar.first->second; ast &res = bar.first->second;
if(bar.second){ if(bar.second){
symb g = sym(e); if(op(e) == Uninterpreted){
if(g == rotate_sum){ symb g = sym(e);
if(var == get_placeholder(arg(e,0))){ if(g == rotate_sum){
res = e; 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); int nargs = num_args(e);
std::vector<ast> args(nargs); std::vector<ast> args(nargs);
@ -792,6 +806,7 @@ class iz3proof_itp_impl : public iz3proof_itp {
return simplify_sum(args); return simplify_sum(args);
} }
ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){
if(pl == arg(pf,1)){ if(pl == arg(pf,1)){
ast cond = mk_true(); ast cond = mk_true();
@ -1034,6 +1049,8 @@ class iz3proof_itp_impl : public iz3proof_itp {
ast mc = z3_simplify(chain_side_proves(LitA,pref)); ast mc = z3_simplify(chain_side_proves(LitA,pref));
Aproves = my_and(Aproves,mc); Aproves = my_and(Aproves,mc);
} }
if(is_true(nc))
return itp;
return make_normal(itp,nc); return make_normal(itp,nc);
} }
@ -2616,6 +2633,11 @@ class iz3proof_itp_impl : public iz3proof_itp {
} }
hash_map<ast,ast>::iterator it = localization_map.find(e); hash_map<ast,ast>::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()){ if(it != localization_map.end()){
pf = localization_pf_map[e]; pf = localization_pf_map[e];
e = it->second; e = it->second;

View file

@ -74,6 +74,8 @@ def_module_params('fixedpoint',
('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'), ('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'),
('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'), ('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'),
('profile', BOOL, False, 'DUALITY: profile run time'), ('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'), ('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'),
)) ))

View file

@ -64,20 +64,22 @@ namespace Duality {
std::vector<expr> clauses; std::vector<expr> clauses;
std::vector<std::vector<RPFP::label_struct> > clause_labels; std::vector<std::vector<RPFP::label_struct> > clause_labels;
hash_map<RPFP::Edge *,int> map; // edges to clauses hash_map<RPFP::Edge *,int> map; // edges to clauses
Solver *old_rs;
Solver::Counterexample cex; Solver::Counterexample cex;
duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) {
ls = 0; ls = 0;
rpfp = 0; rpfp = 0;
status = StatusNull; status = StatusNull;
old_rs = 0;
} }
~duality_data(){ ~duality_data(){
if(old_rs)
dealloc(old_rs);
if(rpfp) if(rpfp)
dealloc(rpfp); dealloc(rpfp);
if(ls) if(ls)
dealloc(ls); dealloc(ls);
if(cex.tree)
delete cex.tree;
} }
}; };
@ -132,15 +134,18 @@ lbool dl_interface::query(::expr * query) {
m_ctx.ensure_opened(); m_ctx.ensure_opened();
// if there is old data, get the cex and dispose (later) // if there is old data, get the cex and dispose (later)
Solver::Counterexample old_cex;
duality_data *old_data = _d; duality_data *old_data = _d;
if(old_data) Solver *old_rs = 0;
old_cex = old_data->cex; 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()); scoped_proof generate_proofs_please(m_ctx.get_manager());
// make a new problem and solver // make a new problem and solver
_d = alloc(duality_data,m_ctx.get_manager()); _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->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx);
_d->rpfp = alloc(RPFP,_d->ls); _d->rpfp = alloc(RPFP,_d->ls);
@ -195,8 +200,9 @@ lbool dl_interface::query(::expr * query) {
Solver *rs = Solver::Create("duality", _d->rpfp); 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 // set its options
IF_VERBOSE(1, rs->SetOption("report","1");); IF_VERBOSE(1, rs->SetOption("report","1"););
rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0"); rs->SetOption("full_expand",m_ctx.get_params().full_expand() ? "1" : "0");
@ -204,6 +210,7 @@ lbool dl_interface::query(::expr * query) {
rs->SetOption("feasible_edges",m_ctx.get_params().feasible_edges() ? "1" : "0"); 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("use_underapprox",m_ctx.get_params().use_underapprox() ? "1" : "0");
rs->SetOption("stratified_inlining",m_ctx.get_params().stratified_inlining() ? "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(); unsigned rb = m_ctx.get_params().recursion_bound();
if(rb != UINT_MAX){ if(rb != UINT_MAX){
std::ostringstream os; os << rb; std::ostringstream os; os << rb;
@ -229,15 +236,14 @@ lbool dl_interface::query(::expr * query) {
// save the result and counterexample if there is one // save the result and counterexample if there is one
_d->status = ans ? StatusModel : StatusRefutation; _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){ if(old_data){
old_data->cex.tree = 0; // we own it now dealloc(old_data); // this deallocates the old solver if there is one
dealloc(old_data);
} }
// dealloc(rs); this is now owned by data
dealloc(rs);
// true means the RPFP problem is SAT, so the query is UNSAT // true means the RPFP problem is SAT, so the query is UNSAT
return ans ? l_false : l_true; return ans ? l_false : l_true;
@ -265,18 +271,16 @@ void dl_interface::reset_statistics() {
static hash_set<func_decl> *local_func_decls; static hash_set<func_decl> *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; context &ctx = d->dd()->ctx;
RPFP::Node &node = *cex.root; RPFP::Node &node = *root;
RPFP::Edge &edge = *node.Outgoing; RPFP::Edge &edge = *node.Outgoing;
// first, prove the children (that are actually used) // first, prove the children (that are actually used)
for(unsigned i = 0; i < edge.Children.size(); i++){ for(unsigned i = 0; i < edge.Children.size(); i++){
if(!cex.tree->Empty(edge.Children[i])){ if(!tree->Empty(edge.Children[i])){
Solver::Counterexample foo = cex; print_proof(d,out,tree,edge.Children[i]);
foo.root = edge.Children[i];
print_proof(d,out,foo);
} }
} }
@ -285,7 +289,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp
out << "(step s!" << node.number; out << "(step s!" << node.number;
out << " (" << node.Name.name(); out << " (" << node.Name.name();
for(unsigned i = 0; i < edge.F.IndParams.size(); i++) 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"; out << ")\n";
// print the rule number // print the rule number
@ -307,8 +311,8 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp
sort the_sort = t.get_quantifier_bound_sort(j); sort the_sort = t.get_quantifier_bound_sort(j);
symbol name = t.get_quantifier_bound_name(j); symbol name = t.get_quantifier_bound_name(j);
expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort));
out << " (= " << skolem << " " << cex.tree->Eval(&edge,skolem) << ")\n"; out << " (= " << skolem << " " << tree->Eval(&edge,skolem) << ")\n";
expr local_skolem = cex.tree->Localize(&edge,skolem); expr local_skolem = tree->Localize(&edge,skolem);
(*local_func_decls).insert(local_skolem.decl()); (*local_func_decls).insert(local_skolem.decl());
} }
} }
@ -316,7 +320,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp
out << " (labels"; out << " (labels";
std::vector<symbol> labels; std::vector<symbol> labels;
cex.tree->GetLabels(&edge,labels); tree->GetLabels(&edge,labels);
for(unsigned j = 0; j < labels.size(); j++){ for(unsigned j = 0; j < labels.size(); j++){
out << " " << labels[j]; out << " " << labels[j];
} }
@ -328,7 +332,7 @@ static void print_proof(dl_interface *d, std::ostream& out, Solver::Counterexamp
out << " (ref "; out << " (ref ";
for(unsigned i = 0; i < edge.Children.size(); i++){ 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; out << " s!" << edge.Children[i]->number;
else else
out << " true"; out << " true";
@ -353,11 +357,11 @@ void dl_interface::display_certificate_non_const(std::ostream& out) {
// negation of the query is the last clause -- prove it // negation of the query is the last clause -- prove it
hash_set<func_decl> locals; hash_set<func_decl> locals;
local_func_decls = &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 << ")\n";
out << "(model \n\""; out << "(model \n\"";
::model mod(m_ctx.get_manager()); ::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++){ for(unsigned i = 0; i < orig_model.num_consts(); i++){
func_decl cnst = orig_model.get_const_decl(i); func_decl cnst = orig_model.get_const_decl(i);
if(locals.find(cnst) == locals.end()){ if(locals.find(cnst) == locals.end()){
@ -428,10 +432,10 @@ model_ref dl_interface::get_model() {
return md; 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; context &ctx = d->dd()->ctx;
ast_manager &mgr = ctx.m(); ast_manager &mgr = ctx.m();
RPFP::Node &node = *cex.root; RPFP::Node &node = *root;
RPFP::Edge &edge = *node.Outgoing; RPFP::Edge &edge = *node.Outgoing;
RPFP::Edge *orig_edge = edge.map; RPFP::Edge *orig_edge = edge.map;
@ -453,21 +457,19 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) {
sort the_sort = t.get_quantifier_bound_sort(j); sort the_sort = t.get_quantifier_bound_sort(j);
symbol name = t.get_quantifier_bound_name(j); symbol name = t.get_quantifier_bound_name(j);
expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); 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); expr_ref thing(ctx.uncook(val),mgr);
substs[0].push_back(thing); 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()); (*local_func_decls).insert(local_skolem.decl());
} }
} }
svector<std::pair<unsigned, unsigned> > pos; svector<std::pair<unsigned, unsigned> > pos;
for(unsigned i = 0; i < edge.Children.size(); i++){ 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<unsigned,unsigned>(i+1,0)); pos.push_back(std::pair<unsigned,unsigned>(i+1,0));
Solver::Counterexample foo = cex; proof_ref prem = extract_proof(d,tree,edge.Children[i]);
foo.root = edge.Children[i];
proof_ref prem = extract_proof(d,foo);
prems.push_back(prem); prems.push_back(prem);
substs.push_back(expr_ref_vector(mgr)); substs.push_back(expr_ref_vector(mgr));
} }
@ -476,7 +478,7 @@ static proof_ref extract_proof(dl_interface *d, Solver::Counterexample &cex) {
func_decl f = node.Name; func_decl f = node.Name;
std::vector<expr> args; std::vector<expr> args;
for(unsigned i = 0; i < edge.F.IndParams.size(); i++) 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); expr conc = f(args);
@ -493,7 +495,7 @@ proof_ref dl_interface::get_proof() {
if(_d->status == StatusRefutation){ if(_d->status == StatusRefutation){
hash_set<func_decl> locals; hash_set<func_decl> locals;
local_func_decls = &locals; local_func_decls = &locals;
return extract_proof(this,_d->cex); return extract_proof(this,_d->cex.get_tree(),_d->cex.get_root());
} }
else else
return proof_ref(m_ctx.get_manager()); return proof_ref(m_ctx.get_manager());