3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-08 10:25:18 +00:00

duality: eager deduction and history-based conjectures

This commit is contained in:
Ken McMillan 2014-03-14 13:40:31 -07:00
parent 180f55bbda
commit bbab6be280
5 changed files with 440 additions and 108 deletions

View file

@ -204,6 +204,9 @@ protected:
/** Is this a background constant? */
virtual bool is_constant(const func_decl &f) = 0;
/** Get the constants in the background vocabulary */
virtual hash_set<func_decl> &get_constants() = 0;
/** Assert a background axiom. */
virtual void assert_axiom(const expr &axiom) = 0;
@ -297,6 +300,11 @@ protected:
return bckg.find(f) != bckg.end();
}
/** Get the constants in the background vocabulary */
virtual hash_set<func_decl> &get_constants(){
return bckg;
}
~iZ3LogicSolver(){
// delete ictx;
delete islvr;
@ -1064,13 +1072,40 @@ protected:
public:
struct Counterexample {
class Counterexample {
private:
RPFP *tree;
RPFP::Node *root;
public:
Counterexample(){
tree = 0;
root = 0;
}
Counterexample(RPFP *_tree, RPFP::Node *_root){
tree = _tree;
root = _root;
}
~Counterexample(){
if(tree) delete tree;
}
void swap(Counterexample &other){
std::swap(tree,other.tree);
std::swap(root,other.root);
}
void set(RPFP *_tree, RPFP::Node *_root){
if(tree) delete tree;
tree = _tree;
root = _root;
}
void clear(){
if(tree) delete tree;
tree = 0;
}
RPFP *get_tree() const {return tree;}
RPFP::Node *get_root() const {return root;}
private:
Counterexample &operator=(const Counterexample &);
Counterexample(const Counterexample &);
};
/** Solve the problem. You can optionally give an old
@ -1080,7 +1115,7 @@ protected:
virtual bool Solve() = 0;
virtual Counterexample GetCounterexample() = 0;
virtual Counterexample &GetCounterexample() = 0;
virtual bool SetOption(const std::string &option, const std::string &value) = 0;
@ -1088,7 +1123,7 @@ protected:
is chiefly useful for abstraction refinement, when we want to
solve a series of similar problems. */
virtual void LearnFrom(Counterexample &old_cex) = 0;
virtual void LearnFrom(Solver *old_solver) = 0;
virtual ~Solver(){}

View file

@ -3334,7 +3334,7 @@ namespace Duality {
func_decl f = t.decl();
std::vector<Term> args;
int nargs = t.num_args();
if(nargs == 0)
if(nargs == 0 && f.get_decl_kind() == Uninterpreted)
ls->declare_constant(f); // keep track of background constants
for(int i = 0; i < nargs; i++)
args.push_back(SubstBoundRec(memo, subst, level, t.arg(i)));

View file

@ -54,6 +54,7 @@ Revision History:
// #define KEEP_EXPANSIONS
// #define USE_CACHING_RPFP
// #define PROPAGATE_BEFORE_CHECK
#define NEW_STRATIFIED_INLINING
#define USE_RPFP_CLONE
#define USE_NEW_GEN_CANDS
@ -82,7 +83,7 @@ namespace Duality {
rpfp = _rpfp;
}
virtual void Extend(RPFP::Node *node){}
virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){}
virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){}
virtual void Bound(RPFP::Node *node){}
virtual void Expand(RPFP::Edge *edge){}
virtual void AddCover(RPFP::Node *covered, std::vector<RPFP::Node *> &covering){}
@ -94,6 +95,7 @@ namespace Duality {
virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){}
virtual void Reject(RPFP::Edge *edge, const std::vector<RPFP::Node *> &Children){}
virtual void Message(const std::string &msg){}
virtual void Depth(int){}
virtual ~Reporter(){}
};
@ -124,6 +126,7 @@ namespace Duality {
rpfp = _rpfp;
reporter = 0;
heuristic = 0;
unwinding = 0;
FullExpand = false;
NoConj = false;
FeasibleEdges = true;
@ -152,6 +155,7 @@ namespace Duality {
#ifdef USE_NEW_GEN_CANDS
delete gen_cands_rpfp;
#endif
if(unwinding) delete unwinding;
}
#ifdef USE_RPFP_CLONE
@ -250,6 +254,19 @@ namespace Duality {
virtual void Done() {}
};
/** The Proposer class proposes conjectures eagerly. These can come
from any source, including predicate abstraction, templates, or
previous solver runs. The proposed conjectures are checked
with low effort when the unwinding is expanded.
*/
class Proposer {
public:
/** Given a node in the unwinding, propose some conjectures */
virtual std::vector<RPFP::Transformer> &Propose(Node *node) = 0;
virtual ~Proposer(){};
};
class Covering; // see below
@ -279,6 +296,7 @@ namespace Duality {
hash_map<Node *, Node *> underapprox_map; // maps underapprox nodes to the nodes they approximate
int last_decisions;
hash_set<Node *> overapproxes;
std::vector<Proposer *> proposers;
#ifdef BOUNDED
struct Counter {
@ -293,24 +311,22 @@ namespace Duality {
virtual bool Solve(){
reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp);
#ifndef LOCALIZE_CONJECTURES
heuristic = !cex.tree ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex);
heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex);
#else
heuristic = !cex.tree ? (Heuristic *)(new LocalHeuristic(rpfp))
heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp))
: (Heuristic *)(new ReplayHeuristic(rpfp,cex));
#endif
cex.tree = 0; // heuristic now owns it
cex.clear(); // in case we didn't use it for heuristic
if(unwinding) delete unwinding;
unwinding = new RPFP(rpfp->ls);
unwinding->HornClauses = rpfp->HornClauses;
indset = new Covering(this);
last_decisions = 0;
CreateEdgesByChildMap();
CreateLeaves();
#ifndef TOP_DOWN
if(!StratifiedInlining){
if(FeasibleEdges)NullaryCandidates();
else InstantiateAllEdges();
}
void CreateInitialUnwinding();
#else
CreateLeaves();
for(unsigned i = 0; i < leaves.size(); i++)
if(!SatisfyUpperBound(leaves[i]))
return false;
@ -322,11 +338,29 @@ namespace Duality {
// print_profile(std::cout);
delete indset;
delete heuristic;
delete unwinding;
// delete unwinding; // keep the unwinding for future mining of predicates
delete reporter;
for(unsigned i = 0; i < proposers.size(); i++)
delete proposers[i];
return res;
}
void CreateInitialUnwinding(){
if(!StratifiedInlining){
CreateLeaves();
if(FeasibleEdges)NullaryCandidates();
else InstantiateAllEdges();
}
else {
#ifdef NEW_STRATIFIED_INLINING
#else
CreateLeaves();
#endif
}
}
void Cancel(){
// TODO
}
@ -347,15 +381,19 @@ namespace Duality {
}
#endif
virtual void LearnFrom(Counterexample &old_cex){
cex = old_cex;
virtual void LearnFrom(Solver *other_solver){
// get the counterexample as a guide
cex.swap(other_solver->GetCounterexample());
// propose conjectures based on old unwinding
Duality *old_duality = dynamic_cast<Duality *>(other_solver);
if(old_duality)
proposers.push_back(new HistoryProposer(old_duality,this));
}
/** Return the counterexample */
virtual Counterexample GetCounterexample(){
Counterexample res = cex;
cex.tree = 0; // Cex now belongs to caller
return res;
/** Return a reference to the counterexample */
virtual Counterexample &GetCounterexample(){
return cex;
}
// options
@ -519,7 +557,11 @@ namespace Duality {
c.Children.resize(edge->Children.size());
for(unsigned j = 0; j < c.Children.size(); j++)
c.Children[j] = leaf_map[edge->Children[j]];
Extend(c);
Node *new_node;
Extend(c,new_node);
#ifdef EARLY_EXPAND
TryExpandNode(new_node);
#endif
}
for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it)
indset->Add(*it);
@ -771,16 +813,14 @@ namespace Duality {
}
/* For stratified inlining, we need a topological sort of the
nodes. */
hash_map<Node *, int> TopoSort;
int TopoSortCounter;
std::vector<Edge *> SortedEdges;
void DoTopoSortRec(Node *node){
if(TopoSort.find(node) != TopoSort.end())
return;
TopoSort[node] = TopoSortCounter++; // just to break cycles
TopoSort[node] = INT_MAX; // just to break cycles
Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge
if(edge){
std::vector<Node *> &chs = edge->Children;
@ -788,22 +828,81 @@ namespace Duality {
DoTopoSortRec(chs[i]);
}
TopoSort[node] = TopoSortCounter++;
SortedEdges.push_back(edge);
}
void DoTopoSort(){
TopoSort.clear();
SortedEdges.clear();
TopoSortCounter = 0;
for(unsigned i = 0; i < nodes.size(); i++)
DoTopoSortRec(nodes[i]);
}
int StratifiedLeafCount;
#ifdef NEW_STRATIFIED_INLINING
/** Stratified inlining builds an initial layered unwinding before
switching to the LAWI strategy. Currently the number of layers
is one. Only nodes that are the targets of back edges are
consider to be leaves. This assumes we have already computed a
topological sort.
*/
bool DoStratifiedInlining(){
DoTopoSort();
int depth = 1; // TODO: make this an option
std::vector<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
down, trying to satisfy the node bounds. We do this as a pre-pass,
limiting the expansion. If we get a counterexample, we are done,
else we continue as usual expanding the unwinding upward.
*/
int StratifiedLeafCount;
bool DoStratifiedInlining(){
timer_start("StratifiedInlining");
@ -826,6 +925,8 @@ namespace Duality {
return true;
}
#endif
/** Here, we do the downward expansion for stratified inlining */
hash_map<Node *, Node *> LeafMap, StratifiedLeafMap;
@ -912,9 +1013,14 @@ namespace Duality {
}
Candidate cand = candidates.front();
candidates.pop_front();
if(CandidateFeasible(cand))
if(!Extend(cand))
if(CandidateFeasible(cand)){
Node *new_node;
if(!Extend(cand,new_node))
return false;
#ifdef EARLY_EXPAND
TryExpandNode(new_node);
#endif
}
}
}
@ -934,9 +1040,9 @@ namespace Duality {
Node *CreateUnderapproxNode(Node *node){
// cex.tree->ComputeUnderapprox(cex.root,0);
// cex.get_tree()->ComputeUnderapprox(cex.get_root(),0);
RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */);
under_node->Annotation.IntersectWith(cex.root->Underapprox);
under_node->Annotation.IntersectWith(cex.get_root()->Underapprox);
AddThing(under_node->Annotation.Formula);
Edge *e = unwinding->CreateLowerBoundEdge(under_node);
under_node->Annotation.SetFull(); // allow this node to cover others
@ -972,9 +1078,8 @@ namespace Duality {
ExpandNodeFromCoverFail(node);
}
#endif
if(_cex) *_cex = cex;
else delete cex.tree; // delete the cex if not required
cex.tree = 0;
if(_cex) (*_cex).swap(cex); // return the cex if asked
cex.clear(); // throw away the useless cex
node->Bound = save; // put back original bound
timer_stop("ProveConjecture");
return false;
@ -1354,16 +1459,20 @@ namespace Duality {
}
}
bool UpdateNodeToNode(Node *node, Node *top){
if(!node->Annotation.SubsetEq(top->Annotation)){
reporter->Update(node,top->Annotation);
indset->Update(node,top->Annotation);
bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){
if(!node->Annotation.SubsetEq(fact)){
reporter->Update(node,fact,eager);
indset->Update(node,fact);
updated_nodes.insert(node->map);
node->Annotation.IntersectWith(top->Annotation);
node->Annotation.IntersectWith(fact);
return true;
}
return false;
}
bool UpdateNodeToNode(Node *node, Node *top){
return Update(node,top->Annotation);
}
/** Update the unwinding solution, using an interpolant for the
derivation tree. */
@ -1405,8 +1514,7 @@ namespace Duality {
// std::cout << "decisions: " << (end_decs - start_decs) << std::endl;
last_decisions = end_decs - start_decs;
if(res){
cex.tree = dt.tree;
cex.root = dt.top;
cex.set(dt.tree,dt.top); // note tree is now owned by cex
if(UseUnderapprox){
UpdateWithCounterexample(node,dt.tree,dt.top);
}
@ -1418,6 +1526,64 @@ namespace Duality {
delete dtp;
return !res;
}
/* For a given nod in the unwinding, get conjectures from the
proposers and check them locally. Update the node with any true
conjectures.
*/
void DoEagerDeduction(Node *node){
for(unsigned i = 0; i < proposers.size(); i++){
const std::vector<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
use of underapproximations, complete it. */
@ -1426,10 +1592,7 @@ namespace Duality {
DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand);
bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree
if(!res) throw "Duality internal error in BuildFullCex";
if(cex.tree)
delete cex.tree;
cex.tree = dt.tree;
cex.root = dt.top;
cex.set(dt.tree,dt.top);
}
void UpdateBackEdges(Node *node){
@ -1452,25 +1615,23 @@ namespace Duality {
}
/** Extend the unwinding, keeping it solved. */
bool Extend(Candidate &cand){
bool Extend(Candidate &cand, Node *&node){
timer_start("Extend");
Node *node = CreateNodeInstance(cand.edge->Parent);
node = CreateNodeInstance(cand.edge->Parent);
CreateEdgeInstance(cand.edge,node,cand.Children);
UpdateBackEdges(node);
reporter->Extend(node);
bool res = SatisfyUpperBound(node);
DoEagerDeduction(node); // first be eager...
bool res = SatisfyUpperBound(node); // then be lazy
if(res) indset->CloseDescendants(node);
else {
#ifdef UNDERAPPROX_NODES
ExpandUnderapproxNodes(cex.tree, cex.root);
ExpandUnderapproxNodes(cex.get_tree(), cex.get_root());
#endif
if(UseUnderapprox) BuildFullCex(node);
timer_stop("Extend");
return res;
}
#ifdef EARLY_EXPAND
TryExpandNode(node);
#endif
timer_stop("Extend");
return res;
}
@ -1930,6 +2091,7 @@ namespace Duality {
unsigned slvr_level = tree->slvr().get_scope_level();
if(slvr_level != stack.back().level)
throw "stacks out of sync!";
reporter->Depth(stack.size());
// res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop
check_result foo = tree->Check(top);
@ -2459,7 +2621,7 @@ namespace Duality {
}
bool ContainsCex(Node *node, Counterexample &cex){
expr val = cex.tree->Eval(cex.root->Outgoing,node->Annotation.Formula);
expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula);
return eq(val,parent->ctx.bool_val(true));
}
@ -2478,15 +2640,15 @@ namespace Duality {
Node *other = insts[i];
if(CouldCover(node,other)){
reporter()->Forcing(node,other);
if(cex.tree && !ContainsCex(other,cex))
if(cex.get_tree() && !ContainsCex(other,cex))
continue;
if(cex.tree) {delete cex.tree; cex.tree = 0;}
cex.clear();
if(parent->ProveConjecture(node,other->Annotation,other,&cex))
if(CloseDescendants(node))
return true;
}
}
if(cex.tree) {delete cex.tree; cex.tree = 0;}
cex.clear();
return false;
}
#else
@ -2585,13 +2747,12 @@ namespace Duality {
Counterexample old_cex;
public:
ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex)
: Heuristic(_rpfp), old_cex(_old_cex)
: Heuristic(_rpfp)
{
old_cex.swap(_old_cex); // take ownership from caller
}
~ReplayHeuristic(){
if(old_cex.tree)
delete old_cex.tree;
}
// Maps nodes of derivation tree into old cex
@ -2599,9 +2760,7 @@ namespace Duality {
void Done() {
cex_map.clear();
if(old_cex.tree)
delete old_cex.tree;
old_cex.tree = 0; // only replay once!
old_cex.clear();
}
void ShowNodeAndChildren(Node *n){
@ -2623,7 +2782,7 @@ namespace Duality {
}
virtual void ChooseExpand(const std::set<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);
return;
}
@ -2632,7 +2791,7 @@ namespace Duality {
for(std::set<Node *>::iterator it = choices.begin(), en = choices.end(); it != en; ++it){
Node *node = (*it);
if(cex_map.empty())
cex_map[node] = old_cex.root; // match the root nodes
cex_map[node] = old_cex.get_root(); // match the root nodes
if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node
Node *parent = node->Incoming[0]->Parent; // assumes we are a tree!
if(cex_map.find(parent) == cex_map.end())
@ -2658,7 +2817,7 @@ namespace Duality {
Node *old_node = cex_map[node];
if(!old_node)
unmatched.insert(node);
else if(old_cex.tree->Empty(old_node))
else if(old_cex.get_tree()->Empty(old_node))
unmatched.insert(node);
else
matched.insert(node);
@ -2732,7 +2891,120 @@ namespace Duality {
}
};
/** This proposer class generates conjectures based on the
unwinding generated by a previous solver. The assumption is
that the provious solver was working on a different
abstraction of the same system. The trick is to adapt the
annotations in the old unwinding to the new predicates. We
start by generating a map from predicates and parameters in
the old problem to the new.
HACK: mapping is done by cheesy name comparison.
*/
class HistoryProposer : public Proposer
{
Duality *old_solver;
Duality *new_solver;
hash_map<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;
public:
StreamReporter(RPFP *_rpfp, std::ostream &_s)
: Reporter(_rpfp), s(_s) {event = 0;}
: Reporter(_rpfp), s(_s) {event = 0; depth = -1;}
int event;
int depth;
void ev(){
s << "[" << event++ << "]" ;
}
@ -2752,23 +3025,30 @@ namespace Duality {
s << " " << rps[i]->number;
s << std::endl;
}
virtual void Update(RPFP::Node *node, const RPFP::Transformer &update){
virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){
ev(); s << "update " << node->number << " " << node->Name.name() << ": ";
rpfp->Summarize(update.Formula);
std::cout << std::endl;
if(depth > 0) s << " (depth=" << depth << ")";
if(eager) s << " (eager)";
s << std::endl;
}
virtual void Bound(RPFP::Node *node){
ev(); s << "check " << node->number << std::endl;
}
virtual void Expand(RPFP::Edge *edge){
RPFP::Node *node = edge->Parent;
ev(); s << "expand " << node->map->number << " " << node->Name.name() << std::endl;
ev(); s << "expand " << node->map->number << " " << node->Name.name();
if(depth > 0) s << " (depth=" << depth << ")";
s << std::endl;
}
virtual void Depth(int d){
depth = d;
}
virtual void AddCover(RPFP::Node *covered, std::vector<RPFP::Node *> &covering){
ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by ";
for(unsigned i = 0; i < covering.size(); i++)
std::cout << covering[i]->number << " ";
std::cout << std::endl;
s << covering[i]->number << " ";
s << std::endl;
}
virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){
ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl;
@ -2779,7 +3059,7 @@ namespace Duality {
virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){
ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": ";
rpfp->Summarize(t.Formula);
std::cout << std::endl;
s << std::endl;
}
virtual void Dominates(RPFP::Node *node, RPFP::Node *other){
ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl;

View file

@ -244,6 +244,9 @@ namespace Duality {
sort_kind get_sort_kind(const sort &s);
expr translate(const expr &e);
func_decl translate(const func_decl &);
void print_expr(std::ostream &s, const ast &e);
fixedpoint mk_fixedpoint();
@ -1374,6 +1377,20 @@ namespace Duality {
return to_expr(a.raw());
}
inline expr context::translate(const expr &e) {
::expr *f = to_expr(e.raw());
if(&e.ctx().m() != &m()) // same ast manager -> no translation
throw "ast manager mismatch";
return cook(f);
}
inline func_decl context::translate(const func_decl &e) {
::func_decl *f = to_func_decl(e.raw());
if(&e.ctx().m() != &m()) // same ast manager -> no translation
throw "ast manager mismatch";
return func_decl(*this,f);
}
typedef double clock_t;
clock_t current_time();
inline void output_time(std::ostream &os, clock_t time){os << time;}

View file

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