mirror of
https://github.com/Z3Prover/z3
synced 2025-06-03 12:51:22 +00:00
duality: eager deduction and history-based conjectures
This commit is contained in:
parent
180f55bbda
commit
bbab6be280
5 changed files with 440 additions and 108 deletions
|
@ -204,6 +204,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;
|
||||||
|
|
||||||
|
@ -297,6 +300,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;
|
||||||
|
@ -1064,13 +1072,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
|
||||||
|
@ -1080,7 +1115,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;
|
||||||
|
|
||||||
|
@ -1088,7 +1123,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(){}
|
||||||
|
|
||||||
|
|
|
@ -3334,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)));
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -152,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
|
||||||
|
@ -250,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
|
||||||
|
|
||||||
|
@ -279,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 {
|
||||||
|
@ -293,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;
|
||||||
|
@ -322,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
|
||||||
}
|
}
|
||||||
|
@ -347,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
|
||||||
|
@ -519,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);
|
||||||
|
@ -771,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;
|
||||||
|
@ -788,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");
|
||||||
|
@ -826,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;
|
||||||
|
@ -912,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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,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
|
||||||
|
@ -972,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;
|
||||||
|
@ -1354,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. */
|
||||||
|
@ -1405,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);
|
||||||
}
|
}
|
||||||
|
@ -1418,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. */
|
||||||
|
@ -1426,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){
|
||||||
|
@ -1452,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;
|
||||||
}
|
}
|
||||||
|
@ -1930,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);
|
||||||
|
@ -2459,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2478,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
|
||||||
|
@ -2585,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
|
||||||
|
@ -2599,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){
|
||||||
|
@ -2623,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;
|
||||||
}
|
}
|
||||||
|
@ -2632,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())
|
||||||
|
@ -2658,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);
|
||||||
|
@ -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<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;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2740,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++ << "]" ;
|
||||||
}
|
}
|
||||||
|
@ -2752,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;
|
||||||
|
@ -2779,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;
|
||||||
|
|
|
@ -244,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();
|
||||||
|
@ -1374,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;}
|
||||||
|
|
|
@ -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,10 +134,12 @@ 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());
|
||||||
|
|
||||||
|
@ -196,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");
|
||||||
|
@ -231,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;
|
||||||
|
@ -267,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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,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
|
||||||
|
@ -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);
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,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];
|
||||||
}
|
}
|
||||||
|
@ -330,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";
|
||||||
|
@ -355,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()){
|
||||||
|
@ -430,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;
|
||||||
|
|
||||||
|
@ -455,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));
|
||||||
}
|
}
|
||||||
|
@ -478,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);
|
||||||
|
|
||||||
|
|
||||||
|
@ -495,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());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue